mirror of
https://github.com/androidx/media.git
synced 2025-05-14 19:19:58 +08:00
Allow continuous seeking.
PiperOrigin-RevId: 419629912
This commit is contained in:
parent
34ed8e2b5f
commit
90912b0710
@ -141,6 +141,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@RtspState private int rtspState;
|
@RtspState private int rtspState;
|
||||||
private boolean hasUpdatedTimelineAndTracks;
|
private boolean hasUpdatedTimelineAndTracks;
|
||||||
private boolean receivedAuthorizationRequest;
|
private boolean receivedAuthorizationRequest;
|
||||||
|
private boolean hasPendingPauseRequest;
|
||||||
private long pendingSeekPositionUs;
|
private long pendingSeekPositionUs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -235,7 +236,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* @param positionUs The seek time measured in microseconds.
|
* @param positionUs The seek time measured in microseconds.
|
||||||
*/
|
*/
|
||||||
public void seekToUs(long positionUs) {
|
public void seekToUs(long positionUs) {
|
||||||
|
// RTSP state is PLAYING after sending out a PAUSE, before receiving the PAUSE response. Sends
|
||||||
|
// out PAUSE only when state PLAYING and no PAUSE is sent.
|
||||||
|
if (rtspState == RTSP_STATE_PLAYING && !hasPendingPauseRequest) {
|
||||||
messageSender.sendPauseRequest(uri, checkNotNull(sessionId));
|
messageSender.sendPauseRequest(uri, checkNotNull(sessionId));
|
||||||
|
}
|
||||||
pendingSeekPositionUs = positionUs;
|
pendingSeekPositionUs = positionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,6 +404,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
sendRequest(
|
sendRequest(
|
||||||
getRequestWithCommonHeaders(
|
getRequestWithCommonHeaders(
|
||||||
METHOD_PAUSE, sessionId, /* additionalHeaders= */ ImmutableMap.of(), uri));
|
METHOD_PAUSE, sessionId, /* additionalHeaders= */ ImmutableMap.of(), uri));
|
||||||
|
hasPendingPauseRequest = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void retryLastRequest() {
|
public void retryLastRequest() {
|
||||||
@ -690,15 +696,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
keepAliveMonitor.start();
|
keepAliveMonitor.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pendingSeekPositionUs = C.TIME_UNSET;
|
||||||
|
// onPlaybackStarted could initiate another seek request, which will set
|
||||||
|
// pendingSeekPositionUs.
|
||||||
playbackEventListener.onPlaybackStarted(
|
playbackEventListener.onPlaybackStarted(
|
||||||
Util.msToUs(response.sessionTiming.startTimeMs), response.trackTimingList);
|
Util.msToUs(response.sessionTiming.startTimeMs), response.trackTimingList);
|
||||||
pendingSeekPositionUs = C.TIME_UNSET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPauseResponseReceived() {
|
private void onPauseResponseReceived() {
|
||||||
checkState(rtspState == RTSP_STATE_PLAYING);
|
checkState(rtspState == RTSP_STATE_PLAYING);
|
||||||
|
|
||||||
rtspState = RTSP_STATE_READY;
|
rtspState = RTSP_STATE_READY;
|
||||||
|
hasPendingPauseRequest = false;
|
||||||
if (pendingSeekPositionUs != C.TIME_UNSET) {
|
if (pendingSeekPositionUs != C.TIME_UNSET) {
|
||||||
startPlayback(Util.usToMs(pendingSeekPositionUs));
|
startPlayback(Util.usToMs(pendingSeekPositionUs));
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@Nullable private IOException preparationError;
|
@Nullable private IOException preparationError;
|
||||||
@Nullable private RtspPlaybackException playbackException;
|
@Nullable private RtspPlaybackException playbackException;
|
||||||
|
|
||||||
private long lastSeekPositionUs;
|
private long requestedSeekPositionUs;
|
||||||
private long pendingSeekPositionUs;
|
private long pendingSeekPositionUs;
|
||||||
|
private long pendingSeekPositionUsForTcpRetry;
|
||||||
private boolean loadingFinished;
|
private boolean loadingFinished;
|
||||||
private boolean released;
|
private boolean released;
|
||||||
private boolean prepared;
|
private boolean prepared;
|
||||||
@ -132,6 +133,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
selectedLoadInfos = new ArrayList<>();
|
selectedLoadInfos = new ArrayList<>();
|
||||||
|
|
||||||
pendingSeekPositionUs = C.TIME_UNSET;
|
pendingSeekPositionUs = C.TIME_UNSET;
|
||||||
|
requestedSeekPositionUs = C.TIME_UNSET;
|
||||||
|
pendingSeekPositionUsForTcpRetry = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Releases the {@link RtspMediaPeriod}. */
|
/** Releases the {@link RtspMediaPeriod}. */
|
||||||
@ -245,17 +248,52 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long seekToUs(long positionUs) {
|
public long seekToUs(long positionUs) {
|
||||||
|
// Handles all RTSP seeking cases:
|
||||||
|
// 1. Seek before the first RTP/UDP packet is received. The seek position is cached to be used
|
||||||
|
// after retrying playback with RTP/TCP.
|
||||||
|
// 2a. Normal RTSP seek: if no additional seek is requested after the first seek. Request RTSP
|
||||||
|
// PAUSE and then PLAY at the seek position.
|
||||||
|
// 2b. If additional seek is requested after the first seek, records the new seek position,
|
||||||
|
// 2b.1. If RTSP PLAY (for the first seek) is already sent, the new seek position is used to
|
||||||
|
// initiate another seek upon receiving PLAY response by invoking this method again.
|
||||||
|
// 2b.2. If RTSP PLAY (for the first seek) has not been sent, the new seek position will be
|
||||||
|
// used in the following PLAY request.
|
||||||
|
|
||||||
|
// TODO(internal: b/198620566) Handle initial seek.
|
||||||
|
// TODO(internal: b/213153670) Handle dropped seek position.
|
||||||
|
if (getBufferedPositionUs() == 0 && !isUsingRtpTcp) {
|
||||||
|
// Stores the seek position for later, if no RTP packet is received when using UDP.
|
||||||
|
pendingSeekPositionUsForTcpRetry = positionUs;
|
||||||
|
return positionUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
discardBuffer(positionUs, /* toKeyframe= */ false);
|
||||||
|
requestedSeekPositionUs = positionUs;
|
||||||
|
|
||||||
if (isSeekPending()) {
|
if (isSeekPending()) {
|
||||||
// TODO(internal b/172331505) Allow seek when a seek is pending.
|
switch (rtspClient.getState()) {
|
||||||
// Does not allow another seek if a seek is pending.
|
case RtspClient.RTSP_STATE_READY:
|
||||||
return pendingSeekPositionUs;
|
// PLAY request is sent, yet to receive the response. requestedSeekPositionUs stores the
|
||||||
|
// new position to do another seek upon receiving the PLAY response.
|
||||||
|
return positionUs;
|
||||||
|
case RtspClient.RTSP_STATE_PLAYING:
|
||||||
|
// Pending PAUSE response, updates client with the newest seek position for the following
|
||||||
|
// PLAY request.
|
||||||
|
pendingSeekPositionUs = positionUs;
|
||||||
|
rtspClient.seekToUs(pendingSeekPositionUs);
|
||||||
|
return positionUs;
|
||||||
|
case RtspClient.RTSP_STATE_UNINITIALIZED:
|
||||||
|
case RtspClient.RTSP_STATE_INIT:
|
||||||
|
default:
|
||||||
|
// Never happens.
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seekInsideBufferUs(positionUs)) {
|
if (seekInsideBufferUs(positionUs)) {
|
||||||
return positionUs;
|
return positionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSeekPositionUs = positionUs;
|
|
||||||
pendingSeekPositionUs = positionUs;
|
pendingSeekPositionUs = positionUs;
|
||||||
rtspClient.seekToUs(positionUs);
|
rtspClient.seekToUs(positionUs);
|
||||||
for (int i = 0; i < rtspLoaderWrappers.size(); i++) {
|
for (int i = 0; i < rtspLoaderWrappers.size(); i++) {
|
||||||
@ -275,8 +313,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return C.TIME_END_OF_SOURCE;
|
return C.TIME_END_OF_SOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSeekPending()) {
|
if (requestedSeekPositionUs != C.TIME_UNSET) {
|
||||||
return pendingSeekPositionUs;
|
return requestedSeekPositionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean allLoaderWrappersAreCanceled = true;
|
boolean allLoaderWrappersAreCanceled = true;
|
||||||
@ -290,7 +328,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
return allLoaderWrappersAreCanceled || bufferedPositionUs == Long.MIN_VALUE
|
return allLoaderWrappersAreCanceled || bufferedPositionUs == Long.MIN_VALUE
|
||||||
? lastSeekPositionUs
|
? 0
|
||||||
: bufferedPositionUs;
|
: bufferedPositionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +479,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@Override
|
@Override
|
||||||
public void onLoadCompleted(
|
public void onLoadCompleted(
|
||||||
RtpDataLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
RtpDataLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
||||||
// TODO(b/172331505) Allow for retry when loading is not ending.
|
|
||||||
if (getBufferedPositionUs() == 0) {
|
if (getBufferedPositionUs() == 0) {
|
||||||
if (!isUsingRtpTcp) {
|
if (!isUsingRtpTcp) {
|
||||||
// Retry playback with TCP if no sample has been received so far, and we are not already
|
// Retry playback with TCP if no sample has been received so far, and we are not already
|
||||||
@ -537,13 +574,26 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
dataLoadable.setTimestamp(trackTiming.rtpTimestamp);
|
dataLoadable.setTimestamp(trackTiming.rtpTimestamp);
|
||||||
dataLoadable.setSequenceNumber(trackTiming.sequenceNumber);
|
dataLoadable.setSequenceNumber(trackTiming.sequenceNumber);
|
||||||
|
|
||||||
if (isSeekPending()) {
|
if (isSeekPending() && pendingSeekPositionUs == requestedSeekPositionUs) {
|
||||||
|
// Seek loadable only when all pending seeks are processed, or SampleQueues will report
|
||||||
|
// inconsistent bufferedPosition.
|
||||||
dataLoadable.seekToUs(startPositionUs, trackTiming.rtpTimestamp);
|
dataLoadable.seekToUs(startPositionUs, trackTiming.rtpTimestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSeekPending()) {
|
if (isSeekPending()) {
|
||||||
|
if (pendingSeekPositionUs == requestedSeekPositionUs) {
|
||||||
|
// No seek request was made after the current pending seek.
|
||||||
pendingSeekPositionUs = C.TIME_UNSET;
|
pendingSeekPositionUs = C.TIME_UNSET;
|
||||||
|
requestedSeekPositionUs = C.TIME_UNSET;
|
||||||
|
} else {
|
||||||
|
// Resets pendingSeekPositionUs to perform a fresh RTSP seek.
|
||||||
|
pendingSeekPositionUs = C.TIME_UNSET;
|
||||||
|
seekToUs(requestedSeekPositionUs);
|
||||||
|
}
|
||||||
|
} else if (pendingSeekPositionUsForTcpRetry != C.TIME_UNSET) {
|
||||||
|
seekToUs(pendingSeekPositionUsForTcpRetry);
|
||||||
|
pendingSeekPositionUsForTcpRetry = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user