Fix player hanging when seeking at MediaItem transition

Before this CL, the video sink was stuck if a flush was executed:
- after VideoSink.onInputStreamChanged, which is setting
  pendingInputStreamBufferPresentationTimeUs and
- before the previous stream was fully rendered.

This is because pendingInputStreamBufferPresentationTimeUs was not reset
to TIME_UNSET when flushing the sink, so that the sink was still waiting
for the last frame of the previous stream to be rendered in
handleInputFrame/Bitmap.

PiperOrigin-RevId: 667924517
This commit is contained in:
kimvde 2024-08-27 03:57:51 -07:00 committed by Copybara-Service
parent c4930c4bb6
commit a4d0735d4c

View File

@ -488,6 +488,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
private long lastBufferPresentationTimeUs; private long lastBufferPresentationTimeUs;
private boolean hasRegisteredFirstInputStream; private boolean hasRegisteredFirstInputStream;
private boolean isInputStreamChangePending;
private long pendingInputStreamBufferPresentationTimeUs; private long pendingInputStreamBufferPresentationTimeUs;
private VideoSink.Listener listener; private VideoSink.Listener listener;
private Executor listenerExecutor; private Executor listenerExecutor;
@ -559,10 +560,11 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
if (resetPosition) { if (resetPosition) {
videoFrameReleaseControl.reset(); videoFrameReleaseControl.reset();
} }
pendingInputStreamBufferPresentationTimeUs = C.TIME_UNSET;
// Don't change input stream offset or reset the pending input stream offset change so that // Don't change input stream offset or reset the pending input stream offset change so that
// it's announced with the next input frame. // it's announced with the next input frame.
// Don't reset pendingInputStreamBufferPresentationTimeUs because it's not guaranteed to // Don't reset isInputStreamChangePending because it's not guaranteed to receive a new input
// receive a new input stream after seeking. // stream after seeking.
} }
@Override @Override
@ -598,10 +600,12 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
// If an input stream registration is pending and seek causes a format change, execution // If an input stream registration is pending and seek causes a format change, execution
// reaches here before registerInputFrame(). Reset pendingInputStreamTimestampUs to // reaches here before registerInputFrame(). Reset pendingInputStreamTimestampUs to
// avoid registering the same input stream again in registerInputFrame(). // avoid registering the same input stream again in registerInputFrame().
isInputStreamChangePending = false;
pendingInputStreamBufferPresentationTimeUs = C.TIME_UNSET; pendingInputStreamBufferPresentationTimeUs = C.TIME_UNSET;
} else { } else {
// If we reach this point, we must have registered at least one frame for processing. // If we reach this point, we must have registered at least one frame for processing.
checkState(lastBufferPresentationTimeUs != C.TIME_UNSET); checkState(lastBufferPresentationTimeUs != C.TIME_UNSET);
isInputStreamChangePending = true;
pendingInputStreamBufferPresentationTimeUs = lastBufferPresentationTimeUs; pendingInputStreamBufferPresentationTimeUs = lastBufferPresentationTimeUs;
} }
} }
@ -728,10 +732,12 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
// An input stream is fully decoded, wait until all of its frames are released before queueing // An input stream is fully decoded, wait until all of its frames are released before queueing
// input frame from the next input stream. // input frame from the next input stream.
if (pendingInputStreamBufferPresentationTimeUs != C.TIME_UNSET) { if (isInputStreamChangePending) {
if (CompositingVideoSinkProvider.this.hasReleasedFrame( if (pendingInputStreamBufferPresentationTimeUs == C.TIME_UNSET
|| CompositingVideoSinkProvider.this.hasReleasedFrame(
pendingInputStreamBufferPresentationTimeUs)) { pendingInputStreamBufferPresentationTimeUs)) {
maybeRegisterInputStream(); maybeRegisterInputStream();
isInputStreamChangePending = false;
pendingInputStreamBufferPresentationTimeUs = C.TIME_UNSET; pendingInputStreamBufferPresentationTimeUs = C.TIME_UNSET;
} else { } else {
return false; return false;
@ -822,14 +828,16 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
* registration, hence it's safe to queue images or frames to the video graph input. * registration, hence it's safe to queue images or frames to the video graph input.
*/ */
private boolean maybeRegisterPendingInputStream() { private boolean maybeRegisterPendingInputStream() {
if (pendingInputStreamBufferPresentationTimeUs == C.TIME_UNSET) { if (!isInputStreamChangePending) {
return true; return true;
} }
// An input stream is fully decoded, wait until all of its frames are released before queueing // An input stream is fully decoded, wait until all of its frames are released before queueing
// input frame from the next input stream. // input frame from the next input stream.
if (CompositingVideoSinkProvider.this.hasReleasedFrame( if (pendingInputStreamBufferPresentationTimeUs == C.TIME_UNSET
|| CompositingVideoSinkProvider.this.hasReleasedFrame(
pendingInputStreamBufferPresentationTimeUs)) { pendingInputStreamBufferPresentationTimeUs)) {
maybeRegisterInputStream(); maybeRegisterInputStream();
isInputStreamChangePending = false;
pendingInputStreamBufferPresentationTimeUs = C.TIME_UNSET; pendingInputStreamBufferPresentationTimeUs = C.TIME_UNSET;
return true; return true;
} }