Commit ff7dcbd6 by claincly Committed by Christos Tsilopoulos

Handle RTSP 301/302 redirection.

PiperOrigin-RevId: 396303242
parent 76014cf0
...@@ -101,8 +101,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -101,8 +101,6 @@ 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 Uri uri;
@Nullable private final RtspAuthUserInfo rtspAuthUserInfo;
private final String userAgent; private final String userAgent;
private final boolean debugLoggingEnabled; private final boolean debugLoggingEnabled;
private final ArrayDeque<RtpLoadInfo> pendingSetupRtpLoadInfos; private final ArrayDeque<RtpLoadInfo> pendingSetupRtpLoadInfos;
...@@ -110,7 +108,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -110,7 +108,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final SparseArray<RtspRequest> pendingRequests; private final SparseArray<RtspRequest> pendingRequests;
private final MessageSender messageSender; private final MessageSender messageSender;
/** RTSP session URI. */
private Uri uri;
private RtspMessageChannel messageChannel; private RtspMessageChannel messageChannel;
@Nullable private RtspAuthUserInfo rtspAuthUserInfo;
@Nullable private String sessionId; @Nullable private String sessionId;
@Nullable private KeepAliveMonitor keepAliveMonitor; @Nullable private KeepAliveMonitor keepAliveMonitor;
@Nullable private RtspAuthenticationInfo rtspAuthenticationInfo; @Nullable private RtspAuthenticationInfo rtspAuthenticationInfo;
...@@ -140,15 +142,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -140,15 +142,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
boolean debugLoggingEnabled) { boolean debugLoggingEnabled) {
this.sessionInfoListener = sessionInfoListener; this.sessionInfoListener = sessionInfoListener;
this.playbackEventListener = playbackEventListener; this.playbackEventListener = playbackEventListener;
this.uri = RtspMessageUtil.removeUserInfo(uri);
this.rtspAuthUserInfo = RtspMessageUtil.parseUserInfo(uri);
this.userAgent = userAgent; this.userAgent = userAgent;
this.debugLoggingEnabled = debugLoggingEnabled; this.debugLoggingEnabled = debugLoggingEnabled;
pendingSetupRtpLoadInfos = new ArrayDeque<>(); this.pendingSetupRtpLoadInfos = new ArrayDeque<>();
pendingRequests = new SparseArray<>(); this.pendingRequests = new SparseArray<>();
messageSender = new MessageSender(); this.messageSender = new MessageSender();
pendingSeekPositionUs = C.TIME_UNSET; this.uri = RtspMessageUtil.removeUserInfo(uri);
messageChannel = new RtspMessageChannel(new MessageListener()); this.messageChannel = new RtspMessageChannel(new MessageListener());
this.rtspAuthUserInfo = RtspMessageUtil.parseUserInfo(uri);
this.pendingSeekPositionUs = C.TIME_UNSET;
} }
/** /**
...@@ -482,6 +484,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -482,6 +484,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
switch (response.status) { switch (response.status) {
case 200: case 200:
break; break;
case 301:
case 302:
// Redirection request.
@Nullable String redirectionUriString = response.headers.get(RtspHeaders.LOCATION);
if (redirectionUriString == null) {
sessionInfoListener.onSessionTimelineRequestFailed(
"Redirection without new location.", /* cause= */ null);
} else {
Uri redirectionUri = Uri.parse(redirectionUriString);
RtspClient.this.uri = RtspMessageUtil.removeUserInfo(redirectionUri);
RtspClient.this.rtspAuthUserInfo = RtspMessageUtil.parseUserInfo(redirectionUri);
messageSender.sendDescribeRequest(RtspClient.this.uri, RtspClient.this.sessionId);
}
return;
case 401: case 401:
if (rtspAuthUserInfo != null && !receivedAuthorizationRequest) { if (rtspAuthUserInfo != null && !receivedAuthorizationRequest) {
// Unauthorized. // Unauthorized.
......
...@@ -50,6 +50,7 @@ import java.util.Map; ...@@ -50,6 +50,7 @@ import java.util.Map;
public static final String CSEQ = "CSeq"; public static final String CSEQ = "CSeq";
public static final String DATE = "Date"; public static final String DATE = "Date";
public static final String EXPIRES = "Expires"; public static final String EXPIRES = "Expires";
public static final String LOCATION = "Location";
public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
public static final String PROXY_REQUIRE = "Proxy-Require"; public static final String PROXY_REQUIRE = "Proxy-Require";
public static final String PUBLIC = "Public"; public static final String PUBLIC = "Public";
...@@ -251,6 +252,8 @@ import java.util.Map; ...@@ -251,6 +252,8 @@ import java.util.Map;
return DATE; return DATE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, EXPIRES)) { } else if (Ascii.equalsIgnoreCase(messageHeaderName, EXPIRES)) {
return EXPIRES; return EXPIRES;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, LOCATION)) {
return LOCATION;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, PROXY_AUTHENTICATE)) { } else if (Ascii.equalsIgnoreCase(messageHeaderName, PROXY_AUTHENTICATE)) {
return PROXY_AUTHENTICATE; return PROXY_AUTHENTICATE;
} else if (Ascii.equalsIgnoreCase(messageHeaderName, PROXY_REQUIRE)) { } else if (Ascii.equalsIgnoreCase(messageHeaderName, PROXY_REQUIRE)) {
......
...@@ -462,6 +462,10 @@ import java.util.regex.Pattern; ...@@ -462,6 +462,10 @@ import java.util.regex.Pattern;
switch (statusCode) { switch (statusCode) {
case 200: case 200:
return "OK"; return "OK";
case 301:
return "Move Permanently";
case 302:
return "Move Temporarily";
case 400: case 400:
return "Bad Request"; return "Bad Request";
case 401: case 401:
......
...@@ -121,6 +121,56 @@ public final class RtspClientTest { ...@@ -121,6 +121,56 @@ public final class RtspClientTest {
} }
@Test @Test
public void connectServerAndClient_describeRedirects_updatesSessionTimeline() throws Exception {
class ResponseProvider implements RtspServer.ResponseProvider {
@Override
public RtspResponse getOptionsResponse() {
return new RtspResponse(/* status= */ 200, RtspHeaders.EMPTY);
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
if (!requestedUri.getPath().contains("redirect")) {
return new RtspResponse(
301,
new RtspHeaders.Builder()
.add(
RtspHeaders.LOCATION,
requestedUri.buildUpon().appendEncodedPath("redirect").build().toString())
.build());
}
return RtspTestUtils.newDescribeResponseWithSdpMessage(
SESSION_DESCRIPTION, rtpPacketStreamDumps, requestedUri);
}
}
rtspServer = new RtspServer(new ResponseProvider());
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()),
/* debugLoggingEnabled= */ false);
rtspClient.start();
RobolectricUtil.runMainLooperUntil(() -> tracksInSession.get() != null);
assertThat(tracksInSession.get()).hasSize(2);
}
@Test
public void public void
connectServerAndClient_serverSupportsDescribeNoHeaderInOptions_updatesSessionTimeline() connectServerAndClient_serverSupportsDescribeNoHeaderInOptions_updatesSessionTimeline()
throws Exception { throws Exception {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment