diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 609d414ae5..9deaf6ce06 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -54,6 +54,9 @@ * DASH extension: * Smooth Streaming extension: * RTSP extension: + * Add support for URI with RTSPT scheme as a way to configure the RTSP + session to use TCP + ([#1484](https://github.com/androidx/media/issues/1484)). * Decoder extensions (FFmpeg, VP9, AV1, etc.): * MIDI extension: * Leanback extension: diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index eca114ec3e..36fc4d19aa 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -2570,7 +2570,8 @@ public final class Util { */ public static @ContentType int inferContentType(Uri uri) { @Nullable String scheme = uri.getScheme(); - if (scheme != null && Ascii.equalsIgnoreCase("rtsp", scheme)) { + if (scheme != null + && (Ascii.equalsIgnoreCase("rtsp", scheme) || Ascii.equalsIgnoreCase("rtspt", scheme))) { return C.CONTENT_TYPE_RTSP; } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaSource.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaSource.java index 7b18c34ef4..9264e0d393 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaSource.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaSource.java @@ -40,6 +40,7 @@ import androidx.media3.exoplayer.source.MediaSourceFactory; import androidx.media3.exoplayer.source.SinglePeriodTimeline; import androidx.media3.exoplayer.upstream.Allocator; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; +import com.google.common.base.Ascii; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import javax.net.SocketFactory; @@ -182,13 +183,21 @@ public final class RtspMediaSource extends BaseMediaSource { checkNotNull(mediaItem.localConfiguration); return new RtspMediaSource( mediaItem, - forceUseRtpTcp + shouldForceUseRtpTcp(mediaItem) ? new TransferRtpDataChannelFactory(timeoutMs) : new UdpDataSourceRtpDataChannelFactory(timeoutMs), userAgent, socketFactory, debugLoggingEnabled); } + + private boolean shouldForceUseRtpTcp(MediaItem mediaItem) { + if (forceUseRtpTcp) { + return true; + } + @Nullable String scheme = checkNotNull(mediaItem.localConfiguration).uri.getScheme(); + return scheme != null && Ascii.equalsIgnoreCase("rtspt", scheme); + } } /** Thrown when an exception or error is encountered during loading an RTSP stream. */ @@ -237,7 +246,7 @@ public final class RtspMediaSource extends BaseMediaSource { this.mediaItem = mediaItem; this.rtpDataChannelFactory = rtpDataChannelFactory; this.userAgent = userAgent; - this.uri = checkNotNull(mediaItem.localConfiguration).uri; + this.uri = maybeConvertRtsptUriScheme(checkNotNull(mediaItem.localConfiguration).uri); this.socketFactory = socketFactory; this.debugLoggingEnabled = debugLoggingEnabled; this.timelineDurationUs = C.TIME_UNSET; @@ -262,7 +271,8 @@ public final class RtspMediaSource extends BaseMediaSource { @Override public boolean canUpdateMediaItem(MediaItem mediaItem) { @Nullable MediaItem.LocalConfiguration newConfiguration = mediaItem.localConfiguration; - return newConfiguration != null && newConfiguration.uri.equals(this.uri); + return newConfiguration != null + && maybeConvertRtsptUriScheme(newConfiguration.uri).equals(this.uri); } @Override @@ -309,6 +319,14 @@ public final class RtspMediaSource extends BaseMediaSource { // Internal methods. + private static Uri maybeConvertRtsptUriScheme(Uri uri) { + @Nullable String scheme = uri.getScheme(); + if (scheme == null || !Ascii.equalsIgnoreCase("rtspt", scheme)) { + return uri; + } + return Uri.parse("rtsp" + uri.toString().substring(5)); + } + private void notifySourceInfoRefreshed() { Timeline timeline = new SinglePeriodTimeline( diff --git a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspMediaSourceTest.java b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspMediaSourceTest.java index 2da542a5ab..6f9d63b0c9 100644 --- a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspMediaSourceTest.java +++ b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspMediaSourceTest.java @@ -66,6 +66,18 @@ public class RtspMediaSourceTest { assertThat(canUpdateMediaItem).isFalse(); } + @Test + public void canUpdateMediaItem_withChangeToRtspFromRtspt_returnsTrue() { + MediaItem initialMediaItem = new MediaItem.Builder().setUri("rtspt://test.test").build(); + MediaItem updatedMediaItem = + TestUtil.buildFullyCustomizedMediaItem().buildUpon().setUri("rtsp://test.test").build(); + MediaSource mediaSource = buildMediaSource(initialMediaItem); + + boolean canUpdateMediaItem = mediaSource.canUpdateMediaItem(updatedMediaItem); + + assertThat(canUpdateMediaItem).isTrue(); + } + @Test public void updateMediaItem_createsTimelineWithUpdatedItem() throws Exception { MediaItem initialMediaItem =