Skip to content

Commit

Permalink
Handle absolute URI in RtspMediaTrack.
Browse files Browse the repository at this point in the history
Issue: #9183

RFC2326 Section C.1.1 specifies that the URI to identify a track can be either
absolute (like rtsp://example.com/path) or relative (like "path"). Currently
we don't handle absolute URI, and this CL is to add the support.

Note though, we don't currently use the Content-Base or Content-Location
headers for the session URI.

PiperOrigin-RevId: 384649818
  • Loading branch information
claincly authored and icbaker committed Jul 20, 2021
1 parent 0f3818e commit d4c62f3
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 18 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
* Fix session description (SDP) parsing to use a HashMap-like behaviour
for duplicated attributes.
([#9014](https://github.com/google/ExoPlayer/issues/9014)).
* Allow using absolute URI in the control attribute in a media description
([#9183](https://github.com/google/ExoPlayer/issues/9183)).

### 2.14.1 (2021-06-11)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
/** Prefix for the RFC6381 codecs string for AVC formats. */
private static final String H264_CODECS_PREFIX = "avc1.";

private static final String GENERIC_CONTROL_ATTR = "*";

/** The track's associated {@link RtpPayloadFormat}. */
public final RtpPayloadFormat payloadFormat;
/** The track's URI. */
Expand All @@ -62,11 +64,7 @@
public RtspMediaTrack(MediaDescription mediaDescription, Uri sessionUri) {
checkArgument(mediaDescription.attributes.containsKey(ATTR_CONTROL));
payloadFormat = generatePayloadFormat(mediaDescription);
uri =
sessionUri
.buildUpon()
.appendEncodedPath(castNonNull(mediaDescription.attributes.get(ATTR_CONTROL)))
.build();
uri = extractTrackUri(sessionUri, castNonNull(mediaDescription.attributes.get(ATTR_CONTROL)));
}

@Override
Expand Down Expand Up @@ -219,4 +217,24 @@ private static byte[] getH264InitializationDataFromParameterSet(String parameter
decodedParameterNalData.length);
return decodedParameterNalUnit;
}

/**
* Extracts the track URI.
*
* <p>The processing logic is specified in RFC2326 Section C.1.1.
*
* @param sessionUri The session URI.
* @param controlAttributeString The control attribute from the track's {@link MediaDescription}.
* @return The extracted track URI.
*/
private static Uri extractTrackUri(Uri sessionUri, String controlAttributeString) {
Uri controlAttributeUri = Uri.parse(controlAttributeString);
if (controlAttributeUri.isAbsolute()) {
return controlAttributeUri;
} else if (controlAttributeString.equals(GENERIC_CONTROL_ATTR)) {
return sessionUri;
} else {
return sessionUri.buildUpon().appendEncodedPath(controlAttributeString).build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AacUtil;
Expand All @@ -38,9 +39,10 @@
public class RtspMediaTrackTest {

@Test
public void generatePayloadFormat_withH264MediaDescription_succeeds() throws Exception {
public void generatePayloadFormat_withH264MediaDescription_succeeds() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_VIDEO, 0, RTP_AVP_PROFILE, 96)
new MediaDescription.Builder(
MEDIA_TYPE_VIDEO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 96)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(500_000)
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
Expand Down Expand Up @@ -79,9 +81,10 @@ public void generatePayloadFormat_withH264MediaDescription_succeeds() throws Exc
}

@Test
public void generatePayloadFormat_withAacMediaDescription_succeeds() throws Exception {
public void generatePayloadFormat_withAacMediaDescription_succeeds() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_AUDIO, 0, RTP_AVP_PROFILE, 97)
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 97)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(96_000)
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
Expand Down Expand Up @@ -122,10 +125,10 @@ public void generatePayloadFormat_withAacMediaDescription_succeeds() throws Exce
}

@Test
public void generatePayloadFormat_withAc3MediaDescriptionWithDefaultChannelCount_succeeds()
throws Exception {
public void generatePayloadFormat_withAc3MediaDescriptionWithDefaultChannelCount_succeeds() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_AUDIO, 0, RTP_AVP_PROFILE, 97)
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 97)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(48_000)
.addAttribute(ATTR_RTPMAP, "97 AC3/48000")
Expand All @@ -149,9 +152,10 @@ public void generatePayloadFormat_withAc3MediaDescriptionWithDefaultChannelCount
}

@Test
public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exception {
public void generatePayloadFormat_withAc3MediaDescription_succeeds() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_AUDIO, 0, RTP_AVP_PROFILE, 97)
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 97)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(48_000)
.addAttribute(ATTR_RTPMAP, "97 AC3/48000/2")
Expand All @@ -174,6 +178,35 @@ public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exce
assertThat(format).isEqualTo(expectedFormat);
}

@Test
public void rtspMediaTrack_mediaDescriptionContainsRelativeUri_setsCorrectTrackUri() {
MediaDescription mediaDescription =
createGenericMediaDescriptionWithControlAttribute("path1/track2");

RtspMediaTrack mediaTrack = new RtspMediaTrack(mediaDescription, Uri.parse("rtsp://test.com"));

assertThat(mediaTrack.uri).isEqualTo(Uri.parse("rtsp://test.com/path1/track2"));
}

@Test
public void rtspMediaTrack_mediaDescriptionContainsAbsoluteUri_setsCorrectTrackUri() {
MediaDescription mediaDescription =
createGenericMediaDescriptionWithControlAttribute("rtsp://test.com/foo");

RtspMediaTrack mediaTrack = new RtspMediaTrack(mediaDescription, Uri.parse("rtsp://test.com"));

assertThat(mediaTrack.uri).isEqualTo(Uri.parse("rtsp://test.com/foo"));
}

@Test
public void rtspMediaTrack_mediaDescriptionContainsGenericUri_setsCorrectTrackUri() {
MediaDescription mediaDescription = createGenericMediaDescriptionWithControlAttribute("*");

RtspMediaTrack mediaTrack = new RtspMediaTrack(mediaDescription, Uri.parse("rtsp://test.com"));

assertThat(mediaTrack.uri).isEqualTo(Uri.parse("rtsp://test.com"));
}

@Test
public void
generatePayloadFormat_withH264MediaDescriptionMissingProfileLevel_generatesCorrectProfileLevel() {
Expand All @@ -195,7 +228,8 @@ public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exce
public void
generatePayloadFormat_withAacMediaDescriptionMissingFmtpAttribute_throwsIllegalArgumentException() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_AUDIO, 0, RTP_AVP_PROFILE, 97)
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 97)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(96_000)
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
Expand All @@ -210,7 +244,8 @@ public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exce
public void
generatePayloadFormat_withMediaDescriptionMissingProfileLevel_throwsIllegalArgumentException() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_AUDIO, 0, RTP_AVP_PROFILE, 97)
new MediaDescription.Builder(
MEDIA_TYPE_AUDIO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 97)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(96_000)
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
Expand All @@ -228,7 +263,8 @@ public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exce
public void
generatePayloadFormat_withH264MediaDescriptionMissingFmtpAttribute_throwsIllegalArgumentException() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_VIDEO, 0, RTP_AVP_PROFILE, 96)
new MediaDescription.Builder(
MEDIA_TYPE_VIDEO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 96)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(500_000)
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
Expand All @@ -243,7 +279,8 @@ public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exce
public void
generatePayloadFormat_withH264MediaDescriptionMissingSpropParameter_throwsIllegalArgumentException() {
MediaDescription mediaDescription =
new MediaDescription.Builder(MEDIA_TYPE_VIDEO, 0, RTP_AVP_PROFILE, 96)
new MediaDescription.Builder(
MEDIA_TYPE_VIDEO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 96)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(500_000)
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
Expand All @@ -254,4 +291,15 @@ public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exce
IllegalArgumentException.class,
() -> RtspMediaTrack.generatePayloadFormat(mediaDescription));
}

private static MediaDescription createGenericMediaDescriptionWithControlAttribute(
String controlAttribute) {
return new MediaDescription.Builder(
MEDIA_TYPE_AUDIO, /* port= */ 0, RTP_AVP_PROFILE, /* payloadType= */ 97)
.setConnection("IN IP4 0.0.0.0")
.setBitrate(48_000)
.addAttribute(ATTR_RTPMAP, "97 AC3/48000/6")
.addAttribute(ATTR_CONTROL, controlAttribute)
.build();
}
}

0 comments on commit d4c62f3

Please sign in to comment.