Skip to content

Commit

Permalink
Retry RTSP Setup with TCP if response with UDP is UnsupportedTransport
Browse files Browse the repository at this point in the history
If RTSP Setup Request with UDP receives HTTP Error Status 461 UnsupportedTransport, then client will retry with TCP.

Issue: #11069
PiperOrigin-RevId: 518807829
(cherry picked from commit bbd45c8)
  • Loading branch information
microkatz authored and rohitjoins committed Apr 18, 2023
1 parent c16840a commit 9e2ad62
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.rtsp.RtspMediaPeriod.RtpLoadInfo;
import com.google.android.exoplayer2.source.rtsp.RtspMediaSource.RtspPlaybackException;
import com.google.android.exoplayer2.source.rtsp.RtspMediaSource.RtspUdpUnsupportedTransportException;
import com.google.android.exoplayer2.source.rtsp.RtspMessageChannel.InterleavedBinaryDataListener;
import com.google.android.exoplayer2.source.rtsp.RtspMessageUtil.RtspAuthUserInfo;
import com.google.android.exoplayer2.source.rtsp.RtspMessageUtil.RtspSessionHeader;
Expand Down Expand Up @@ -577,8 +578,24 @@ private void handleRtspResponse(List<String> message) {
receivedAuthorizationRequest = true;
return;
}
// fall through: if unauthorized and no userInfo present, or previous authentication
// unsuccessful.
// if unauthorized and no userInfo present, or previous authentication
// unsuccessful, then dispatch RtspPlaybackException
dispatchRtspError(
new RtspPlaybackException(
RtspMessageUtil.toMethodString(requestMethod) + " " + response.status));
return;
case 461:
String exceptionMessage =
RtspMessageUtil.toMethodString(requestMethod) + " " + response.status;
// If request was SETUP with UDP transport protocol, then throw
// RtspUdpUnsupportedTransportException.
String transportHeaderValue =
checkNotNull(matchingRequest.headers.get(RtspHeaders.TRANSPORT));
dispatchRtspError(
requestMethod == METHOD_SETUP && !transportHeaderValue.contains("TCP")
? new RtspUdpUnsupportedTransportException(exceptionMessage)
: new RtspPlaybackException(exceptionMessage));
return;
default:
dispatchRtspError(
new RtspPlaybackException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,6 @@ public void onLoadCompleted(
// using TCP. Retrying will setup new loadables, so will not retry with the current
// loadables.
retryWithRtpTcp();
isUsingRtpTcp = true;
}
return;
}
Expand Down Expand Up @@ -642,7 +641,13 @@ public void onPlaybackStarted(

@Override
public void onPlaybackError(RtspPlaybackException error) {
playbackException = error;
if (error instanceof RtspMediaSource.RtspUdpUnsupportedTransportException && !isUsingRtpTcp) {
// Retry playback with TCP if we receive RtspUdpUnsupportedTransportException, and we are
// not already using TCP. Retrying will setup new loadables.
retryWithRtpTcp();
} else {
playbackException = error;
}
}

@Override
Expand All @@ -666,6 +671,9 @@ public void onSessionTimelineRequestFailed(String message, @Nullable Throwable c
}

private void retryWithRtpTcp() {
// Retry should only run once.
isUsingRtpTcp = true;

rtspClient.retryWithRtpTcp();

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public RtspMediaSource createMediaSource(MediaItem mediaItem) {
}

/** Thrown when an exception or error is encountered during loading an RTSP stream. */
public static final class RtspPlaybackException extends IOException {
public static class RtspPlaybackException extends IOException {
public RtspPlaybackException(String message) {
super(message);
}
Expand All @@ -204,6 +204,13 @@ public RtspPlaybackException(String message, Throwable e) {
}
}

/** Thrown when an RTSP Unsupported Transport error (461) is encountered during RTSP Setup. */
public static final class RtspUdpUnsupportedTransportException extends RtspPlaybackException {
public RtspUdpUnsupportedTransportException(String message) {
super(message);
}
}

private final MediaItem mediaItem;
private final RtpDataChannel.Factory rtpDataChannelFactory;
private final String userAgent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,4 +453,77 @@ public void onSessionTimelineRequestFailed(
RobolectricUtil.runMainLooperUntil(timelineRequestFailed::get);
assertThat(rtspClient.getState()).isEqualTo(RtspClient.RTSP_STATE_UNINITIALIZED);
}

@Test
public void connectServerAndClient_describeResponseRequiresAuthentication_doesNotUpdateTimeline()
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, RtspHeaders headers) {
String authorizationHeader = headers.get(RtspHeaders.AUTHORIZATION);
if (authorizationHeader == null) {
return new RtspResponse(
/* status= */ 401,
new RtspHeaders.Builder()
.add(RtspHeaders.CSEQ, headers.get(RtspHeaders.CSEQ))
.add(
RtspHeaders.WWW_AUTHENTICATE,
"Digest realm=\"RTSP server\","
+ " nonce=\"0cdfe9719e7373b7d5bb2913e2115f3f\","
+ " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")
.add(RtspHeaders.WWW_AUTHENTICATE, "BASIC realm=\"WallyWorld\"")
.build());
}
if (!authorizationHeader.contains("Digest")) {
return new RtspResponse(
401,
new RtspHeaders.Builder()
.add(RtspHeaders.CSEQ, headers.get(RtspHeaders.CSEQ))
.build());
}

return RtspTestUtils.newDescribeResponseWithSdpMessage(
"v=0\r\n"
+ "o=- 1606776316530225 1 IN IP4 127.0.0.1\r\n"
+ "s=Exoplayer test\r\n"
+ "t=0 0\r\n"
// The session is 50.46s long.
+ "a=range:npt=0-50.46\r\n",
rtpPacketStreamDumps,
requestedUri);
}
}
rtspServer = new RtspServer(new ResponseProvider());

AtomicBoolean timelineRequestFailed = new AtomicBoolean();
rtspClient =
new RtspClient(
new SessionInfoListener() {
@Override
public void onSessionTimelineUpdated(
RtspSessionTiming timing, ImmutableList<RtspMediaTrack> tracks) {}

@Override
public void onSessionTimelineRequestFailed(
String message, @Nullable Throwable cause) {
timelineRequestFailed.set(true);
}
},
EMPTY_PLAYBACK_LISTENER,
/* userAgent= */ "ExoPlayer:RtspClientTest",
RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false);
rtspClient.start();

RobolectricUtil.runMainLooperUntil(timelineRequestFailed::get);
assertThat(rtspClient.getState()).isEqualTo(RtspClient.RTSP_STATE_UNINITIALIZED);
}
}
Loading

0 comments on commit 9e2ad62

Please sign in to comment.