Add support for RTSPT scheme in RtspMediaSource.Factory

If the `RtspMediaSource.Factory` is provided a `MediaItem` containing a uri with the scheme `rtspt`, then the factory will create its `RtspMediaSource` configured to use TCP.

Issue: androidx/media#1484
PiperOrigin-RevId: 740340604
(cherry picked from commit a220b0cb5e16e6cb8d8719a0e1ffa7e00859abc0)
This commit is contained in:
michaelkatz 2025-03-25 07:30:22 -07:00 committed by tonihei
parent 1d2019b770
commit 2939bfccbe
4 changed files with 38 additions and 4 deletions

View File

@ -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:

View File

@ -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;
}

View File

@ -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(

View File

@ -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 =