mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Handle absolute URI in RtspMediaTrack.
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
This commit is contained in:
parent
ae31ebb143
commit
f9f93c5a49
@ -144,6 +144,9 @@
|
|||||||
* Cronet extension:
|
* Cronet extension:
|
||||||
* Add `CronetDataSource.Factory.setRequestPriority` to allow setting the
|
* Add `CronetDataSource.Factory.setRequestPriority` to allow setting the
|
||||||
priority of requests made by `CronetDataSource` instances.
|
priority of requests made by `CronetDataSource` instances.
|
||||||
|
* RTSP:
|
||||||
|
* 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)
|
### 2.14.1 (2021-06-11)
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
/** Prefix for the RFC6381 codecs string for AVC formats. */
|
/** Prefix for the RFC6381 codecs string for AVC formats. */
|
||||||
private static final String H264_CODECS_PREFIX = "avc1.";
|
private static final String H264_CODECS_PREFIX = "avc1.";
|
||||||
|
|
||||||
|
private static final String GENERIC_CONTROL_ATTR = "*";
|
||||||
|
|
||||||
/** The track's associated {@link RtpPayloadFormat}. */
|
/** The track's associated {@link RtpPayloadFormat}. */
|
||||||
public final RtpPayloadFormat payloadFormat;
|
public final RtpPayloadFormat payloadFormat;
|
||||||
/** The track's URI. */
|
/** The track's URI. */
|
||||||
@ -62,11 +64,7 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
public RtspMediaTrack(MediaDescription mediaDescription, Uri sessionUri) {
|
public RtspMediaTrack(MediaDescription mediaDescription, Uri sessionUri) {
|
||||||
checkArgument(mediaDescription.attributes.containsKey(ATTR_CONTROL));
|
checkArgument(mediaDescription.attributes.containsKey(ATTR_CONTROL));
|
||||||
payloadFormat = generatePayloadFormat(mediaDescription);
|
payloadFormat = generatePayloadFormat(mediaDescription);
|
||||||
uri =
|
uri = extractTrackUri(sessionUri, castNonNull(mediaDescription.attributes.get(ATTR_CONTROL)));
|
||||||
sessionUri
|
|
||||||
.buildUpon()
|
|
||||||
.appendEncodedPath(castNonNull(mediaDescription.attributes.get(ATTR_CONTROL)))
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -219,4 +217,24 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
decodedParameterNalData.length);
|
decodedParameterNalData.length);
|
||||||
return decodedParameterNalUnit;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import static com.google.android.exoplayer2.source.rtsp.SessionDescription.ATTR_
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
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 androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.audio.AacUtil;
|
import com.google.android.exoplayer2.audio.AacUtil;
|
||||||
@ -38,9 +39,10 @@ import org.junit.runner.RunWith;
|
|||||||
public class RtspMediaTrackTest {
|
public class RtspMediaTrackTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generatePayloadFormat_withH264MediaDescription_succeeds() throws Exception {
|
public void generatePayloadFormat_withH264MediaDescription_succeeds() {
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(500_000)
|
.setBitrate(500_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
|
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
|
||||||
@ -79,9 +81,10 @@ public class RtspMediaTrackTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generatePayloadFormat_withAacMediaDescription_succeeds() throws Exception {
|
public void generatePayloadFormat_withAacMediaDescription_succeeds() {
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(96_000)
|
.setBitrate(96_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
|
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
|
||||||
@ -122,10 +125,10 @@ public class RtspMediaTrackTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generatePayloadFormat_withAc3MediaDescriptionWithDefaultChannelCount_succeeds()
|
public void generatePayloadFormat_withAc3MediaDescriptionWithDefaultChannelCount_succeeds() {
|
||||||
throws Exception {
|
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(48_000)
|
.setBitrate(48_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "97 AC3/48000")
|
.addAttribute(ATTR_RTPMAP, "97 AC3/48000")
|
||||||
@ -149,9 +152,10 @@ public class RtspMediaTrackTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generatePayloadFormat_withAc3MediaDescription_succeeds() throws Exception {
|
public void generatePayloadFormat_withAc3MediaDescription_succeeds() {
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(48_000)
|
.setBitrate(48_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "97 AC3/48000/2")
|
.addAttribute(ATTR_RTPMAP, "97 AC3/48000/2")
|
||||||
@ -174,6 +178,35 @@ public class RtspMediaTrackTest {
|
|||||||
assertThat(format).isEqualTo(expectedFormat);
|
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
|
@Test
|
||||||
public void
|
public void
|
||||||
generatePayloadFormat_withH264MediaDescriptionMissingProfileLevel_generatesCorrectProfileLevel() {
|
generatePayloadFormat_withH264MediaDescriptionMissingProfileLevel_generatesCorrectProfileLevel() {
|
||||||
@ -195,7 +228,8 @@ public class RtspMediaTrackTest {
|
|||||||
public void
|
public void
|
||||||
generatePayloadFormat_withAacMediaDescriptionMissingFmtpAttribute_throwsIllegalArgumentException() {
|
generatePayloadFormat_withAacMediaDescriptionMissingFmtpAttribute_throwsIllegalArgumentException() {
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(96_000)
|
.setBitrate(96_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
|
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
|
||||||
@ -210,7 +244,8 @@ public class RtspMediaTrackTest {
|
|||||||
public void
|
public void
|
||||||
generatePayloadFormat_withMediaDescriptionMissingProfileLevel_throwsIllegalArgumentException() {
|
generatePayloadFormat_withMediaDescriptionMissingProfileLevel_throwsIllegalArgumentException() {
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(96_000)
|
.setBitrate(96_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
|
.addAttribute(ATTR_RTPMAP, "97 MPEG4-GENERIC/44100")
|
||||||
@ -228,7 +263,8 @@ public class RtspMediaTrackTest {
|
|||||||
public void
|
public void
|
||||||
generatePayloadFormat_withH264MediaDescriptionMissingFmtpAttribute_throwsIllegalArgumentException() {
|
generatePayloadFormat_withH264MediaDescriptionMissingFmtpAttribute_throwsIllegalArgumentException() {
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(500_000)
|
.setBitrate(500_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
|
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
|
||||||
@ -243,7 +279,8 @@ public class RtspMediaTrackTest {
|
|||||||
public void
|
public void
|
||||||
generatePayloadFormat_withH264MediaDescriptionMissingSpropParameter_throwsIllegalArgumentException() {
|
generatePayloadFormat_withH264MediaDescriptionMissingSpropParameter_throwsIllegalArgumentException() {
|
||||||
MediaDescription mediaDescription =
|
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")
|
.setConnection("IN IP4 0.0.0.0")
|
||||||
.setBitrate(500_000)
|
.setBitrate(500_000)
|
||||||
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
|
.addAttribute(ATTR_RTPMAP, "96 H264/90000")
|
||||||
@ -254,4 +291,15 @@ public class RtspMediaTrackTest {
|
|||||||
IllegalArgumentException.class,
|
IllegalArgumentException.class,
|
||||||
() -> RtspMediaTrack.generatePayloadFormat(mediaDescription));
|
() -> 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user