Allow empty information attributes in RTSP Session Description
Issue: androidx/media#1087 PiperOrigin-RevId: 608534659
This commit is contained in:
parent
04bdf3e814
commit
52c1d60d39
@ -42,6 +42,8 @@
|
|||||||
* DASH Extension:
|
* DASH Extension:
|
||||||
* Smooth Streaming Extension:
|
* Smooth Streaming Extension:
|
||||||
* RTSP Extension:
|
* RTSP Extension:
|
||||||
|
* Skip empty session information values (i-tags) in SDP parsing
|
||||||
|
([#1087](https://github.com/androidx/media/issues/1087)).
|
||||||
* Decoder Extensions (FFmpeg, VP9, AV1, MIDI, etc.):
|
* Decoder Extensions (FFmpeg, VP9, AV1, MIDI, etc.):
|
||||||
* Leanback extension:
|
* Leanback extension:
|
||||||
* Cast Extension:
|
* Cast Extension:
|
||||||
|
@ -26,6 +26,7 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.media3.common.ParserException;
|
import androidx.media3.common.ParserException;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -35,6 +36,8 @@ import java.util.regex.Pattern;
|
|||||||
// SDP line always starts with an one letter tag, followed by an equal sign. The information
|
// SDP line always starts with an one letter tag, followed by an equal sign. The information
|
||||||
// under the given tag follows an optional space.
|
// under the given tag follows an optional space.
|
||||||
private static final Pattern SDP_LINE_PATTERN = Pattern.compile("([a-z])=\\s?(.+)");
|
private static final Pattern SDP_LINE_PATTERN = Pattern.compile("([a-z])=\\s?(.+)");
|
||||||
|
// SDP line with a one letter tag, an equal sign, and an empty value.
|
||||||
|
private static final Pattern SDP_LINE_WITH_EMPTY_VALUE_PATTERN = Pattern.compile("^([a-z])=$");
|
||||||
// Matches an attribute line (with a= sdp tag removed. Example: range:npt=0-50.0).
|
// Matches an attribute line (with a= sdp tag removed. Example: range:npt=0-50.0).
|
||||||
// Attribute can also be a flag, i.e. without a value, like recvonly. Reference RFC4566 Section 9
|
// Attribute can also be a flag, i.e. without a value, like recvonly. Reference RFC4566 Section 9
|
||||||
// Page 43, under "token-char".
|
// Page 43, under "token-char".
|
||||||
@ -81,6 +84,11 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
Matcher matcher = SDP_LINE_PATTERN.matcher(line);
|
Matcher matcher = SDP_LINE_PATTERN.matcher(line);
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
|
Matcher sdpTagMatcher = SDP_LINE_WITH_EMPTY_VALUE_PATTERN.matcher(line);
|
||||||
|
if (sdpTagMatcher.matches() && Objects.equals(sdpTagMatcher.group(1), INFORMATION_TYPE)) {
|
||||||
|
// Allow and skip empty Session Information (tag 'i') attributes
|
||||||
|
continue;
|
||||||
|
}
|
||||||
throw ParserException.createForMalformedManifest(
|
throw ParserException.createForMalformedManifest(
|
||||||
"Malformed SDP line: " + line, /* cause= */ null);
|
"Malformed SDP line: " + line, /* cause= */ null);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import androidx.media3.common.ParserException;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -270,6 +271,38 @@ public class SessionDescriptionTest {
|
|||||||
assertThat(rtpMapAttribute.clockRate).isEqualTo(44100);
|
assertThat(rtpMapAttribute.clockRate).isEqualTo(44100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parse_sdpStringWithEmptyInformationAttribute_succeeds() throws Exception {
|
||||||
|
String testMediaSdpInfo =
|
||||||
|
"v=0\r\n"
|
||||||
|
+ "o=MNobody 2890844526 2890842807 IN IP4 192.0.2.46\r\n"
|
||||||
|
+ "s=SDP Seminar\r\n"
|
||||||
|
+ "i=\r\n"
|
||||||
|
+ "t=0 0\r\n"
|
||||||
|
+ "a=control:*\r\n"
|
||||||
|
+ "m=audio 3456 RTP/AVP 0\r\n"
|
||||||
|
+ "i=\r\n"
|
||||||
|
+ "a=rtpmap:97 AC3/44100 \r\n";
|
||||||
|
|
||||||
|
SessionDescription sessionDescription = SessionDescriptionParser.parse(testMediaSdpInfo);
|
||||||
|
|
||||||
|
assertThat(sessionDescription.sessionInfo).isNull();
|
||||||
|
assertThat(sessionDescription.mediaDescriptionList.get(0).mediaTitle).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parse_sdpStringWithEmptySessionAttribute_throwsParserException() {
|
||||||
|
String testMediaSdpInfo =
|
||||||
|
"v=0\r\n"
|
||||||
|
+ "o=MNobody 2890844526 2890842807 IN IP4 192.0.2.46\r\n"
|
||||||
|
+ "s=\r\n"
|
||||||
|
+ "a=control:*\r\n"
|
||||||
|
+ "m=audio 3456 RTP/AVP 0\r\n"
|
||||||
|
+ "a=rtpmap:97 AC3/44100 \r\n";
|
||||||
|
|
||||||
|
assertThrows(ParserException.class, () -> SessionDescriptionParser.parse(testMediaSdpInfo));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void buildMediaDescription_withInvalidRtpmapAttribute_throwsIllegalStateException() {
|
public void buildMediaDescription_withInvalidRtpmapAttribute_throwsIllegalStateException() {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user