Skip to content

Commit

Permalink
Handle when RTSP track timing is not available.
Browse files Browse the repository at this point in the history
Issue: #9775

We got a few issues for this on GH already. Some RTSP servers do not provide
track timing in PLAY responses, or the timings are invalid.

Missing timing means the RTSP stream is not seekable. Added method to
1. Update the timeline that seek is not possible
2. Report read discontinuity so that playback can start from the beginning.

PiperOrigin-RevId: 423281439
  • Loading branch information
claincly authored and icbaker committed Jan 25, 2022
1 parent 4cf9106 commit 5a60db3
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 14 deletions.
4 changes: 3 additions & 1 deletion RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,10 @@
* RTSP:
* Provide a client API to override the `SocketFactory` used for any server
connection ([#9606](https://github.com/google/ExoPlayer/pull/9606)).
* Prefers DIGEST authentication method over BASIC if both are present.
* Prefers DIGEST authentication method over BASIC if both are present
([#9800](https://github.com/google/ExoPlayer/issues/9800)).
* Handle when RTSP track timing is not available
([#9775](https://github.com/google/ExoPlayer/issues/9775)).
* Cast extension
* Fix bug that prevented `CastPlayer` from calling `onIsPlayingChanged`
correctly ([#9792](https://github.com/google/ExoPlayer/issues/9792)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ interface Listener {

/** Called when the {@link RtspSessionTiming} is available. */
void onSourceInfoRefreshed(RtspSessionTiming timing);

/** Called when the RTSP server does not support seeking. */
default void onSeekingUnsupported() {}
}

/** The maximum times to retry if the underlying data channel failed to bind. */
Expand All @@ -90,6 +93,7 @@ interface Listener {
private long pendingSeekPositionUs;
private long pendingSeekPositionUsForTcpRetry;
private boolean loadingFinished;
private boolean notifyDiscontinuity;
private boolean released;
private boolean prepared;
private boolean trackSelected;
Expand Down Expand Up @@ -243,6 +247,14 @@ public void discardBuffer(long positionUs, boolean toKeyframe) {

@Override
public long readDiscontinuity() {
// Discontinuity only happens in RTSP when seeking an unexpectedly un-seekable RTSP server (a
// server that doesn't include the required RTP-Info header in its PLAY responses). This only
// applies to seeks made before receiving the first RTSP PLAY response. The playback can only
// start from time zero in this case.
if (notifyDiscontinuity) {
notifyDiscontinuity = false;
return 0;
}
return C.TIME_UNSET;
}

Expand Down Expand Up @@ -355,7 +367,7 @@ public void reevaluateBuffer(long positionUs) {
// SampleStream methods.

/* package */ boolean isReady(int trackGroupIndex) {
return rtspLoaderWrappers.get(trackGroupIndex).isSampleQueueReady();
return !suppressRead() && rtspLoaderWrappers.get(trackGroupIndex).isSampleQueueReady();
}

@ReadDataResult
Expand All @@ -364,9 +376,23 @@ public void reevaluateBuffer(long positionUs) {
FormatHolder formatHolder,
DecoderInputBuffer buffer,
@ReadFlags int readFlags) {
if (suppressRead()) {
return C.RESULT_NOTHING_READ;
}
return rtspLoaderWrappers.get(sampleQueueIndex).read(formatHolder, buffer, readFlags);
}

/* package */ int skipData(int sampleQueueIndex, long positionUs) {
if (suppressRead()) {
return C.RESULT_NOTHING_READ;
}
return rtspLoaderWrappers.get(sampleQueueIndex).skipData(positionUs);
}

private boolean suppressRead() {
return notifyDiscontinuity;
}

// Internal methods.

@Nullable
Expand Down Expand Up @@ -549,18 +575,23 @@ public void onRtspSetupCompleted() {
@Override
public void onPlaybackStarted(
long startPositionUs, ImmutableList<RtspTrackTiming> trackTimingList) {
// Validate that the trackTimingList contains timings for the selected tracks.

// Validate that the trackTimingList contains timings for the selected tracks, and notify the
// listener.
ArrayList<String> trackUrisWithTiming = new ArrayList<>(trackTimingList.size());
for (int i = 0; i < trackTimingList.size(); i++) {
trackUrisWithTiming.add(checkNotNull(trackTimingList.get(i).uri.getPath()));
}
for (int i = 0; i < selectedLoadInfos.size(); i++) {
RtpLoadInfo loadInfo = selectedLoadInfos.get(i);
if (!trackUrisWithTiming.contains(loadInfo.getTrackUri().getPath())) {
playbackException =
new RtspPlaybackException(
"Server did not provide timing for track " + loadInfo.getTrackUri());
return;
listener.onSeekingUnsupported();
if (isSeekPending()) {
notifyDiscontinuity = true;
pendingSeekPositionUs = C.TIME_UNSET;
requestedSeekPositionUs = C.TIME_UNSET;
pendingSeekPositionUsForTcpRetry = C.TIME_UNSET;
}
}
}

Expand Down Expand Up @@ -697,7 +728,7 @@ public int readData(

@Override
public int skipData(long positionUs) {
return 0;
return RtspMediaPeriod.this.skipData(track, positionUs);
}
}

Expand Down Expand Up @@ -748,6 +779,12 @@ public int read(
return sampleQueue.read(formatHolder, buffer, readFlags, /* loadingFinished= */ canceled);
}

public int skipData(long positionUs) {
int skipCount = sampleQueue.getSkipCount(positionUs, /* allowEndOfQueue= */ canceled);
sampleQueue.skip(skipCount);
return skipCount;
}

/** Cancels loading. */
public void cancelLoad() {
if (!canceled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,21 @@ public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long star
allocator,
rtpDataChannelFactory,
uri,
/* listener= */ timing -> {
timelineDurationUs = Util.msToUs(timing.getDurationMs());
timelineIsSeekable = !timing.isLive();
timelineIsLive = timing.isLive();
timelineIsPlaceholder = false;
notifySourceInfoRefreshed();
new RtspMediaPeriod.Listener() {
@Override
public void onSourceInfoRefreshed(RtspSessionTiming timing) {
timelineDurationUs = Util.msToUs(timing.getDurationMs());
timelineIsSeekable = !timing.isLive();
timelineIsLive = timing.isLive();
timelineIsPlaceholder = false;
notifySourceInfoRefreshed();
}

@Override
public void onSeekingUnsupported() {
timelineIsSeekable = false;
notifySourceInfoRefreshed();
}
},
userAgent,
socketFactory,
Expand Down

0 comments on commit 5a60db3

Please sign in to comment.