mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add VideoSink.signalEndOfCurrentInputStream()
The isLastBuffer flag passed to MediaCodecRenderer.processOutputBuffer() is unreliable. It is true for the last frames (plural) of a video stream, which makes it possible for the video renderer to end before all the frames have been rendered to the output surface. PiperOrigin-RevId: 700941685
This commit is contained in:
parent
dfe3c90f9a
commit
f3f4646296
@ -111,6 +111,11 @@ import java.util.concurrent.Executor;
|
||||
return videoFrameReleaseControl.isReady(rendererOtherwiseReady);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -1547,6 +1547,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderToEndOfStream() {
|
||||
if (videoSink != null) {
|
||||
videoSink.signalEndOfCurrentInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp that is added to the buffer presentation time (the player decoding
|
||||
* position) to get the frame presentation time, in microseconds.
|
||||
@ -1608,6 +1615,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
protected void onProcessedStreamChange() {
|
||||
super.onProcessedStreamChange();
|
||||
if (videoSink != null) {
|
||||
// Signaling end of the previous stream.
|
||||
videoSink.signalEndOfCurrentInputStream();
|
||||
videoSink.setStreamTimestampInfo(
|
||||
getOutputStreamStartPositionUs(),
|
||||
getBufferTimestampAdjustmentUs(),
|
||||
|
@ -605,6 +605,11 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
||||
/* rendererOtherwiseReady= */ rendererOtherwiseReady && isInitialized());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
finalBufferPresentationTimeUs = lastBufferPresentationTimeUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
return isInitialized()
|
||||
@ -742,9 +747,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
||||
}
|
||||
|
||||
lastBufferPresentationTimeUs = bufferPresentationTimeUs;
|
||||
if (isLastFrame) {
|
||||
finalBufferPresentationTimeUs = bufferPresentationTimeUs;
|
||||
}
|
||||
// Use the frame presentation time as render time so that the SurfaceTexture is accompanied
|
||||
// by this timestamp. Setting a realtime based release time is only relevant when rendering to
|
||||
// a SurfaceView, but we render to a surface in this case.
|
||||
@ -766,7 +768,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
||||
timestampIterator.getLastTimestampUs() - inputBufferTimestampAdjustmentUs;
|
||||
checkState(lastBufferPresentationTimeUs != C.TIME_UNSET);
|
||||
this.lastBufferPresentationTimeUs = lastBufferPresentationTimeUs;
|
||||
finalBufferPresentationTimeUs = lastBufferPresentationTimeUs;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,17 @@ public interface VideoSink {
|
||||
*/
|
||||
boolean isReady(boolean rendererOtherwiseReady);
|
||||
|
||||
/** Returns whether all the data has been rendered to the output surface. */
|
||||
/** Signals the end of the current input stream. */
|
||||
void signalEndOfCurrentInputStream();
|
||||
|
||||
/**
|
||||
* Returns whether all the data has been rendered to the output surface.
|
||||
*
|
||||
* <p>This method returns {@code true} if the end of the last input stream has been {@linkplain
|
||||
* #signalEndOfCurrentInputStream() signaled} and all the input frames have been rendered. Note
|
||||
* that a new input stream can be {@linkplain #onInputStreamChanged(int, Format) signaled} even
|
||||
* when this method returns true (in which case the sink will not be ended anymore).
|
||||
*/
|
||||
boolean isEnded();
|
||||
|
||||
/**
|
||||
@ -251,10 +261,12 @@ public interface VideoSink {
|
||||
* Handles a video input frame.
|
||||
*
|
||||
* <p>Must be called after the corresponding stream is {@linkplain #onInputStreamChanged(int,
|
||||
* Format) signalled}.
|
||||
* Format) signaled}.
|
||||
*
|
||||
* @param framePresentationTimeUs The frame's presentation time, in microseconds.
|
||||
* @param isLastFrame Whether this is the last frame of the video stream.
|
||||
* @param isLastFrame Whether this is the last frame of the video stream. This flag is set on a
|
||||
* best effort basis, and any logic relying on it should degrade gracefully to handle cases
|
||||
* where it's not set.
|
||||
* @param positionUs The current playback position, in microseconds.
|
||||
* @param elapsedRealtimeUs {@link SystemClock#elapsedRealtime()} in microseconds, taken
|
||||
* approximately at the time the playback position was {@code positionUs}.
|
||||
@ -274,7 +286,7 @@ public interface VideoSink {
|
||||
* Handles an input {@link Bitmap}.
|
||||
*
|
||||
* <p>Must be called after the corresponding stream is {@linkplain #onInputStreamChanged(int,
|
||||
* Format) signalled}.
|
||||
* Format) signaled}.
|
||||
*
|
||||
* @param inputBitmap The {@link Bitmap} to queue to the video sink.
|
||||
* @param timestampIterator The times within the current stream that the bitmap should be shown
|
||||
|
@ -134,6 +134,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
return videoSink == null || videoSink.isReady(rendererOtherwiseReady);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
executeOrDelay(VideoSink::signalEndOfCurrentInputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
return videoSink != null && videoSink.isEnded();
|
||||
|
@ -475,7 +475,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
.build());
|
||||
inputStreamPending = false;
|
||||
}
|
||||
return videoSink.handleInputBitmap(outputImage, checkStateNotNull(timestampIterator));
|
||||
if (!videoSink.handleInputBitmap(outputImage, checkStateNotNull(timestampIterator))) {
|
||||
return false;
|
||||
}
|
||||
videoSink.signalEndOfCurrentInputStream();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user