Merge pull request #9606 from ened:rtsp-socket-factory

PiperOrigin-RevId: 413751821
This commit is contained in:
Ian Baker 2021-12-07 16:43:17 +00:00
commit e1eb30c8ba
6 changed files with 119 additions and 2 deletions

View File

@ -123,6 +123,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final SessionInfoListener sessionInfoListener; private final SessionInfoListener sessionInfoListener;
private final PlaybackEventListener playbackEventListener; private final PlaybackEventListener playbackEventListener;
private final String userAgent; private final String userAgent;
private final SocketFactory socketFactory;
private final boolean debugLoggingEnabled; private final boolean debugLoggingEnabled;
private final ArrayDeque<RtpLoadInfo> pendingSetupRtpLoadInfos; private final ArrayDeque<RtpLoadInfo> pendingSetupRtpLoadInfos;
// TODO(b/172331505) Add a timeout monitor for pending requests. // TODO(b/172331505) Add a timeout monitor for pending requests.
@ -155,16 +156,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* @param playbackEventListener The {@link PlaybackEventListener}. * @param playbackEventListener The {@link PlaybackEventListener}.
* @param userAgent The user agent. * @param userAgent The user agent.
* @param uri The RTSP playback URI. * @param uri The RTSP playback URI.
* @param socketFactory A socket factory for the RTSP connection.
* @param debugLoggingEnabled Whether to log RTSP messages.
*/ */
public RtspClient( public RtspClient(
SessionInfoListener sessionInfoListener, SessionInfoListener sessionInfoListener,
PlaybackEventListener playbackEventListener, PlaybackEventListener playbackEventListener,
String userAgent, String userAgent,
Uri uri, Uri uri,
SocketFactory socketFactory,
boolean debugLoggingEnabled) { boolean debugLoggingEnabled) {
this.sessionInfoListener = sessionInfoListener; this.sessionInfoListener = sessionInfoListener;
this.playbackEventListener = playbackEventListener; this.playbackEventListener = playbackEventListener;
this.userAgent = userAgent; this.userAgent = userAgent;
this.socketFactory = socketFactory;
this.debugLoggingEnabled = debugLoggingEnabled; this.debugLoggingEnabled = debugLoggingEnabled;
this.pendingSetupRtpLoadInfos = new ArrayDeque<>(); this.pendingSetupRtpLoadInfos = new ArrayDeque<>();
this.pendingRequests = new SparseArray<>(); this.pendingRequests = new SparseArray<>();
@ -286,10 +291,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} }
/** Returns a {@link Socket} that is connected to the {@code uri}. */ /** Returns a {@link Socket} that is connected to the {@code uri}. */
private static Socket getSocket(Uri uri) throws IOException { private Socket getSocket(Uri uri) throws IOException {
checkArgument(uri.getHost() != null); checkArgument(uri.getHost() != null);
int rtspPort = uri.getPort() > 0 ? uri.getPort() : DEFAULT_RTSP_PORT; int rtspPort = uri.getPort() > 0 ? uri.getPort() : DEFAULT_RTSP_PORT;
return SocketFactory.getDefault().createSocket(checkNotNull(uri.getHost()), rtspPort); return socketFactory.createSocket(checkNotNull(uri.getHost()), rtspPort);
} }
private void dispatchRtspError(Throwable error) { private void dispatchRtspError(Throwable error) {

View File

@ -56,6 +56,7 @@ import java.io.IOException;
import java.net.BindException; import java.net.BindException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.net.SocketFactory;
import org.checkerframework.checker.nullness.compatqual.NullableType; import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@ -104,6 +105,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* @param uri The RTSP playback {@link Uri}. * @param uri The RTSP playback {@link Uri}.
* @param listener A {@link Listener} to receive session information updates. * @param listener A {@link Listener} to receive session information updates.
* @param userAgent The user agent. * @param userAgent The user agent.
* @param socketFactory A socket factory for {@link RtspClient}'s connection.
* @param debugLoggingEnabled Whether to log RTSP messages.
*/ */
public RtspMediaPeriod( public RtspMediaPeriod(
Allocator allocator, Allocator allocator,
@ -111,6 +114,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Uri uri, Uri uri,
Listener listener, Listener listener,
String userAgent, String userAgent,
SocketFactory socketFactory,
boolean debugLoggingEnabled) { boolean debugLoggingEnabled) {
this.allocator = allocator; this.allocator = allocator;
this.rtpDataChannelFactory = rtpDataChannelFactory; this.rtpDataChannelFactory = rtpDataChannelFactory;
@ -124,6 +128,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/* playbackEventListener= */ internalListener, /* playbackEventListener= */ internalListener,
/* userAgent= */ userAgent, /* userAgent= */ userAgent,
/* uri= */ uri, /* uri= */ uri,
socketFactory,
debugLoggingEnabled); debugLoggingEnabled);
rtspLoaderWrappers = new ArrayList<>(); rtspLoaderWrappers = new ArrayList<>();
selectedLoadInfos = new ArrayList<>(); selectedLoadInfos = new ArrayList<>();

View File

@ -42,6 +42,7 @@ import androidx.media3.exoplayer.source.SinglePeriodTimeline;
import androidx.media3.exoplayer.upstream.Allocator; import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import java.io.IOException; import java.io.IOException;
import javax.net.SocketFactory;
/** An Rtsp {@link MediaSource} */ /** An Rtsp {@link MediaSource} */
@UnstableApi @UnstableApi
@ -71,12 +72,14 @@ public final class RtspMediaSource extends BaseMediaSource {
private long timeoutMs; private long timeoutMs;
private String userAgent; private String userAgent;
private SocketFactory socketFactory;
private boolean forceUseRtpTcp; private boolean forceUseRtpTcp;
private boolean debugLoggingEnabled; private boolean debugLoggingEnabled;
public Factory() { public Factory() {
timeoutMs = DEFAULT_TIMEOUT_MS; timeoutMs = DEFAULT_TIMEOUT_MS;
userAgent = MediaLibraryInfo.VERSION_SLASHY; userAgent = MediaLibraryInfo.VERSION_SLASHY;
socketFactory = SocketFactory.getDefault();
} }
/** /**
@ -106,6 +109,18 @@ public final class RtspMediaSource extends BaseMediaSource {
return this; return this;
} }
/**
* Sets a socket factory for {@link RtspClient}'s connection, the default value is {@link
* SocketFactory#getDefault()}.
*
* @param socketFactory A socket factory.
* @return This Factory, for convenience.
*/
public Factory setSocketFactory(SocketFactory socketFactory) {
this.socketFactory = socketFactory;
return this;
}
/** /**
* Sets whether to log RTSP messages, the default value is {@code false}. * Sets whether to log RTSP messages, the default value is {@code false}.
* *
@ -205,6 +220,7 @@ public final class RtspMediaSource extends BaseMediaSource {
? new TransferRtpDataChannelFactory(timeoutMs) ? new TransferRtpDataChannelFactory(timeoutMs)
: new UdpDataSourceRtpDataChannelFactory(timeoutMs), : new UdpDataSourceRtpDataChannelFactory(timeoutMs),
userAgent, userAgent,
socketFactory,
debugLoggingEnabled); debugLoggingEnabled);
} }
} }
@ -228,6 +244,7 @@ public final class RtspMediaSource extends BaseMediaSource {
private final RtpDataChannel.Factory rtpDataChannelFactory; private final RtpDataChannel.Factory rtpDataChannelFactory;
private final String userAgent; private final String userAgent;
private final Uri uri; private final Uri uri;
private final SocketFactory socketFactory;
private final boolean debugLoggingEnabled; private final boolean debugLoggingEnabled;
private long timelineDurationUs; private long timelineDurationUs;
@ -240,11 +257,13 @@ public final class RtspMediaSource extends BaseMediaSource {
MediaItem mediaItem, MediaItem mediaItem,
RtpDataChannel.Factory rtpDataChannelFactory, RtpDataChannel.Factory rtpDataChannelFactory,
String userAgent, String userAgent,
SocketFactory socketFactory,
boolean debugLoggingEnabled) { boolean debugLoggingEnabled) {
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
this.rtpDataChannelFactory = rtpDataChannelFactory; this.rtpDataChannelFactory = rtpDataChannelFactory;
this.userAgent = userAgent; this.userAgent = userAgent;
this.uri = checkNotNull(this.mediaItem.localConfiguration).uri; this.uri = checkNotNull(this.mediaItem.localConfiguration).uri;
this.socketFactory = socketFactory;
this.debugLoggingEnabled = debugLoggingEnabled; this.debugLoggingEnabled = debugLoggingEnabled;
this.timelineDurationUs = C.TIME_UNSET; this.timelineDurationUs = C.TIME_UNSET;
this.timelineIsPlaceholder = true; this.timelineIsPlaceholder = true;
@ -284,6 +303,7 @@ public final class RtspMediaSource extends BaseMediaSource {
notifySourceInfoRefreshed(); notifySourceInfoRefreshed();
}, },
userAgent, userAgent,
socketFactory,
debugLoggingEnabled); debugLoggingEnabled);
} }

View File

@ -27,8 +27,12 @@ import androidx.media3.exoplayer.rtsp.RtspMediaSource.RtspPlaybackException;
import androidx.media3.test.utils.robolectric.RobolectricUtil; import androidx.media3.test.utils.robolectric.RobolectricUtil;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.net.SocketFactory;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -77,6 +81,80 @@ public final class RtspClientTest {
Util.closeQuietly(rtspClient); Util.closeQuietly(rtspClient);
} }
@Test
public void connectServerAndClient_usesCustomSocketFactory() throws Exception {
class ResponseProvider implements RtspServer.ResponseProvider {
@Override
public RtspResponse getOptionsResponse() {
return new RtspResponse(
/* status= */ 200,
new RtspHeaders.Builder().add(RtspHeaders.PUBLIC, "OPTIONS, DESCRIBE").build());
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
return RtspTestUtils.newDescribeResponseWithSdpMessage(
SESSION_DESCRIPTION, rtpPacketStreamDumps, requestedUri);
}
}
rtspServer = new RtspServer(new ResponseProvider());
AtomicBoolean didCallCreateSocket = new AtomicBoolean();
SocketFactory socketFactory =
new SocketFactory() {
@Override
public Socket createSocket(String host, int port) throws IOException {
didCallCreateSocket.set(true);
return SocketFactory.getDefault().createSocket(host, port);
}
@Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i1)
throws IOException {
didCallCreateSocket.set(true);
return SocketFactory.getDefault().createSocket(s, i, inetAddress, i1);
}
@Override
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
didCallCreateSocket.set(true);
return SocketFactory.getDefault().createSocket(inetAddress, i);
}
@Override
public Socket createSocket(
InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
didCallCreateSocket.set(true);
return SocketFactory.getDefault().createSocket(inetAddress, i, inetAddress1, i1);
}
};
AtomicReference<ImmutableList<RtspMediaTrack>> tracksInSession = new AtomicReference<>();
rtspClient =
new RtspClient(
new SessionInfoListener() {
@Override
public void onSessionTimelineUpdated(
RtspSessionTiming timing, ImmutableList<RtspMediaTrack> tracks) {
tracksInSession.set(tracks);
}
@Override
public void onSessionTimelineRequestFailed(
String message, @Nullable Throwable cause) {}
},
EMPTY_PLAYBACK_LISTENER,
/* userAgent= */ "ExoPlayer:RtspClientTest",
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
socketFactory,
/* debugLoggingEnabled= */ false);
rtspClient.start();
RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null);
assertThat(didCallCreateSocket.get()).isTrue();
}
@Test @Test
public void connectServerAndClient_serverSupportsDescribe_updatesSessionTimeline() public void connectServerAndClient_serverSupportsDescribe_updatesSessionTimeline()
throws Exception { throws Exception {
@ -113,6 +191,7 @@ public final class RtspClientTest {
EMPTY_PLAYBACK_LISTENER, EMPTY_PLAYBACK_LISTENER,
/* userAgent= */ "ExoPlayer:RtspClientTest", /* userAgent= */ "ExoPlayer:RtspClientTest",
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()), RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false); /* debugLoggingEnabled= */ false);
rtspClient.start(); rtspClient.start();
RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null); RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null);
@ -164,6 +243,7 @@ public final class RtspClientTest {
EMPTY_PLAYBACK_LISTENER, EMPTY_PLAYBACK_LISTENER,
/* userAgent= */ "ExoPlayer:RtspClientTest", /* userAgent= */ "ExoPlayer:RtspClientTest",
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()), RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false); /* debugLoggingEnabled= */ false);
rtspClient.start(); rtspClient.start();
RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null); RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null);
@ -207,6 +287,7 @@ public final class RtspClientTest {
EMPTY_PLAYBACK_LISTENER, EMPTY_PLAYBACK_LISTENER,
/* userAgent= */ "ExoPlayer:RtspClientTest", /* userAgent= */ "ExoPlayer:RtspClientTest",
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()), RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false); /* debugLoggingEnabled= */ false);
rtspClient.start(); rtspClient.start();
RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null); RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null);
@ -253,6 +334,7 @@ public final class RtspClientTest {
EMPTY_PLAYBACK_LISTENER, EMPTY_PLAYBACK_LISTENER,
/* userAgent= */ "ExoPlayer:RtspClientTest", /* userAgent= */ "ExoPlayer:RtspClientTest",
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()), RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false); /* debugLoggingEnabled= */ false);
rtspClient.start(); rtspClient.start();
RobolectricUtil.runMainLooperUntil(() -> failureMessage.get() != null); RobolectricUtil.runMainLooperUntil(() -> failureMessage.get() != null);
@ -299,6 +381,7 @@ public final class RtspClientTest {
EMPTY_PLAYBACK_LISTENER, EMPTY_PLAYBACK_LISTENER,
/* userAgent= */ "ExoPlayer:RtspClientTest", /* userAgent= */ "ExoPlayer:RtspClientTest",
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()), RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false); /* debugLoggingEnabled= */ false);
rtspClient.start(); rtspClient.start();

View File

@ -27,6 +27,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import javax.net.SocketFactory;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -84,6 +85,7 @@ public final class RtspMediaPeriodTest {
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()), RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
/* listener= */ timing -> refreshedSourceDurationMs.set(timing.getDurationMs()), /* listener= */ timing -> refreshedSourceDurationMs.set(timing.getDurationMs()),
/* userAgent= */ "ExoPlayer:RtspPeriodTest", /* userAgent= */ "ExoPlayer:RtspPeriodTest",
/* socketFactory= */ SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false); /* debugLoggingEnabled= */ false);
mediaPeriod.prepare( mediaPeriod.prepare(

View File

@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.net.SocketFactory;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -156,6 +157,7 @@ public final class RtspPlaybackTest {
MediaItem.fromUri(RtspTestUtils.getTestUri(serverRtspPortNumber)), MediaItem.fromUri(RtspTestUtils.getTestUri(serverRtspPortNumber)),
rtpDataChannelFactory, rtpDataChannelFactory,
"ExoPlayer:PlaybackTest", "ExoPlayer:PlaybackTest",
SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false), /* debugLoggingEnabled= */ false),
false); false);
return player; return player;