mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Implement DefaultVideoSink.isEnded
To do that, I had to add VideoFrameRenderControl.signalEndOfInput() and isEnded() to match the VideoSink interface. PiperOrigin-RevId: 700957098
This commit is contained in:
parent
f3f4646296
commit
852091f2d9
@ -113,12 +113,12 @@ import java.util.concurrent.Executor;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void signalEndOfCurrentInputStream() {
|
public void signalEndOfCurrentInputStream() {
|
||||||
throw new UnsupportedOperationException();
|
videoFrameRenderControl.signalEndOfInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnded() {
|
public boolean isEnded() {
|
||||||
throw new UnsupportedOperationException();
|
return videoFrameRenderControl.isEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -252,6 +252,11 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
private @State int state;
|
private @State int state;
|
||||||
@Nullable private Renderer.WakeupListener wakeupListener;
|
@Nullable private Renderer.WakeupListener wakeupListener;
|
||||||
|
|
||||||
|
/** The buffer presentation time, in microseconds, of the final frame in the stream. */
|
||||||
|
private long finalBufferPresentationTimeUs;
|
||||||
|
|
||||||
|
private boolean hasSignaledEndOfCurrentInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the buffer timestamp (the player position, with renderer offset) to the composition
|
* Converts the buffer timestamp (the player position, with renderer offset) to the composition
|
||||||
* timestamp, in microseconds. The composition time starts from zero, add this adjustment to
|
* timestamp, in microseconds. The composition time starts from zero, add this adjustment to
|
||||||
@ -275,6 +280,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
state = STATE_CREATED;
|
state = STATE_CREATED;
|
||||||
videoGraphOutputFormat = new Format.Builder().build();
|
videoGraphOutputFormat = new Format.Builder().build();
|
||||||
addListener(inputVideoSink);
|
addListener(inputVideoSink);
|
||||||
|
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -378,6 +384,12 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
outputStreamStartPositionUs = newOutputStreamStartPositionUs;
|
outputStreamStartPositionUs = newOutputStreamStartPositionUs;
|
||||||
}
|
}
|
||||||
videoFrameRenderControl.onFrameAvailableForRendering(bufferPresentationTimeUs);
|
videoFrameRenderControl.onFrameAvailableForRendering(bufferPresentationTimeUs);
|
||||||
|
if (finalBufferPresentationTimeUs != C.TIME_UNSET
|
||||||
|
&& bufferPresentationTimeUs >= finalBufferPresentationTimeUs) {
|
||||||
|
// TODO b/257464707 - Support extensively modified media.
|
||||||
|
defaultVideoSink.signalEndOfCurrentInputStream();
|
||||||
|
hasSignaledEndOfCurrentInputStream = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -454,8 +466,10 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
/* rendererOtherwiseReady= */ rendererOtherwiseReady && pendingFlushCount == 0);
|
/* rendererOtherwiseReady= */ rendererOtherwiseReady && pendingFlushCount == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasReleasedFrame(long presentationTimeUs) {
|
private boolean isEnded() {
|
||||||
return pendingFlushCount == 0 && videoFrameRenderControl.hasReleasedFrame(presentationTimeUs);
|
return pendingFlushCount == 0
|
||||||
|
&& hasSignaledEndOfCurrentInputStream
|
||||||
|
&& defaultVideoSink.isEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -484,6 +498,8 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
defaultVideoSink.setStreamTimestampInfo(
|
defaultVideoSink.setStreamTimestampInfo(
|
||||||
lastStartPositionUs, /* unused */ C.TIME_UNSET, /* unused */ C.TIME_UNSET);
|
lastStartPositionUs, /* unused */ C.TIME_UNSET, /* unused */ C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
|
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
|
hasSignaledEndOfCurrentInputStream = false;
|
||||||
// Handle pending video graph callbacks to ensure video size changes reach the video render
|
// Handle pending video graph callbacks to ensure video size changes reach the video render
|
||||||
// control.
|
// control.
|
||||||
checkStateNotNull(handler).post(() -> pendingFlushCount--);
|
checkStateNotNull(handler).post(() -> pendingFlushCount--);
|
||||||
@ -522,9 +538,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
private long inputBufferTimestampAdjustmentUs;
|
private long inputBufferTimestampAdjustmentUs;
|
||||||
private long lastResetPositionUs;
|
private long lastResetPositionUs;
|
||||||
|
|
||||||
/** The buffer presentation time, in microseconds, of the final frame in the stream. */
|
|
||||||
private long finalBufferPresentationTimeUs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer presentation timestamp, in microseconds, of the most recently registered frame.
|
* The buffer presentation timestamp, in microseconds, of the most recently registered frame.
|
||||||
*/
|
*/
|
||||||
@ -541,7 +554,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
videoFrameProcessorMaxPendingFrameCount =
|
videoFrameProcessorMaxPendingFrameCount =
|
||||||
Util.getMaxPendingFramesCountForMediaCodecDecoders(context);
|
Util.getMaxPendingFramesCountForMediaCodecDecoders(context);
|
||||||
videoEffects = ImmutableList.of();
|
videoEffects = ImmutableList.of();
|
||||||
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
|
||||||
lastBufferPresentationTimeUs = C.TIME_UNSET;
|
lastBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
listener = VideoSink.Listener.NO_OP;
|
listener = VideoSink.Listener.NO_OP;
|
||||||
listenerExecutor = NO_OP_EXECUTOR;
|
listenerExecutor = NO_OP_EXECUTOR;
|
||||||
@ -590,7 +602,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
if (isInitialized()) {
|
if (isInitialized()) {
|
||||||
videoFrameProcessor.flush();
|
videoFrameProcessor.flush();
|
||||||
}
|
}
|
||||||
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
|
||||||
lastBufferPresentationTimeUs = C.TIME_UNSET;
|
lastBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
PlaybackVideoGraphWrapper.this.flush(resetPosition);
|
PlaybackVideoGraphWrapper.this.flush(resetPosition);
|
||||||
// Don't change input stream start position or reset the pending input stream timestamp info
|
// Don't change input stream start position or reset the pending input stream timestamp info
|
||||||
@ -612,9 +623,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnded() {
|
public boolean isEnded() {
|
||||||
return isInitialized()
|
return isInitialized() && PlaybackVideoGraphWrapper.this.isEnded();
|
||||||
&& finalBufferPresentationTimeUs != C.TIME_UNSET
|
|
||||||
&& PlaybackVideoGraphWrapper.this.hasReleasedFrame(finalBufferPresentationTimeUs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -630,6 +639,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
this.inputType = inputType;
|
this.inputType = inputType;
|
||||||
this.inputFormat = format;
|
this.inputFormat = format;
|
||||||
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
|
hasSignaledEndOfCurrentInputStream = false;
|
||||||
registerInputStream(format);
|
registerInputStream(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,8 +77,12 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
/** A queue of unprocessed input frame timestamps. */
|
/** A queue of unprocessed input frame timestamps. */
|
||||||
private final LongArrayQueue presentationTimestampsUs;
|
private final LongArrayQueue presentationTimestampsUs;
|
||||||
|
|
||||||
private long lastInputPresentationTimeUs;
|
private long latestInputPresentationTimeUs;
|
||||||
private long lastOutputPresentationTimeUs;
|
private long latestOutputPresentationTimeUs;
|
||||||
|
|
||||||
|
/** The presentation time of the final frame to render. */
|
||||||
|
private long lastPresentationTimeUs;
|
||||||
|
|
||||||
private VideoSize outputVideoSize;
|
private VideoSize outputVideoSize;
|
||||||
private long outputStreamStartPositionUs;
|
private long outputStreamStartPositionUs;
|
||||||
|
|
||||||
@ -91,16 +95,18 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
videoSizes = new TimedValueQueue<>();
|
videoSizes = new TimedValueQueue<>();
|
||||||
streamStartPositionsUs = new TimedValueQueue<>();
|
streamStartPositionsUs = new TimedValueQueue<>();
|
||||||
presentationTimestampsUs = new LongArrayQueue();
|
presentationTimestampsUs = new LongArrayQueue();
|
||||||
lastInputPresentationTimeUs = C.TIME_UNSET;
|
latestInputPresentationTimeUs = C.TIME_UNSET;
|
||||||
outputVideoSize = VideoSize.UNKNOWN;
|
outputVideoSize = VideoSize.UNKNOWN;
|
||||||
lastOutputPresentationTimeUs = C.TIME_UNSET;
|
latestOutputPresentationTimeUs = C.TIME_UNSET;
|
||||||
|
lastPresentationTimeUs = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flushes the renderer. */
|
/** Flushes the renderer. */
|
||||||
public void flush() {
|
public void flush() {
|
||||||
presentationTimestampsUs.clear();
|
presentationTimestampsUs.clear();
|
||||||
lastInputPresentationTimeUs = C.TIME_UNSET;
|
latestInputPresentationTimeUs = C.TIME_UNSET;
|
||||||
lastOutputPresentationTimeUs = C.TIME_UNSET;
|
latestOutputPresentationTimeUs = C.TIME_UNSET;
|
||||||
|
lastPresentationTimeUs = C.TIME_UNSET;
|
||||||
if (streamStartPositionsUs.size() > 0) {
|
if (streamStartPositionsUs.size() > 0) {
|
||||||
// There is a pending streaming start position change. If seeking within the same stream, keep
|
// There is a pending streaming start position change. If seeking within the same stream, keep
|
||||||
// the pending start position with min timestamp to ensure the start position is applied on
|
// the pending start position with min timestamp to ensure the start position is applied on
|
||||||
@ -120,18 +126,6 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the renderer has released a frame after a specific presentation timestamp.
|
|
||||||
*
|
|
||||||
* @param presentationTimeUs The requested timestamp, in microseconds.
|
|
||||||
* @return Whether the renderer has released a frame with a timestamp greater than or equal to
|
|
||||||
* {@code presentationTimeUs}.
|
|
||||||
*/
|
|
||||||
public boolean hasReleasedFrame(long presentationTimeUs) {
|
|
||||||
return lastOutputPresentationTimeUs != C.TIME_UNSET
|
|
||||||
&& lastOutputPresentationTimeUs >= presentationTimeUs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Incrementally renders available video frames.
|
* Incrementally renders available video frames.
|
||||||
*
|
*
|
||||||
@ -160,17 +154,17 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
return;
|
return;
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_SKIP:
|
case VideoFrameReleaseControl.FRAME_RELEASE_SKIP:
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_DROP:
|
case VideoFrameReleaseControl.FRAME_RELEASE_DROP:
|
||||||
lastOutputPresentationTimeUs = presentationTimeUs;
|
latestOutputPresentationTimeUs = presentationTimeUs;
|
||||||
dropFrame();
|
dropFrame();
|
||||||
break;
|
break;
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_IGNORE:
|
case VideoFrameReleaseControl.FRAME_RELEASE_IGNORE:
|
||||||
// TODO b/293873191 - Handle very late buffers and drop to key frame. Need to flush
|
// TODO b/293873191 - Handle very late buffers and drop to key frame. Need to flush
|
||||||
// VideoGraph input frames in this case.
|
// VideoGraph input frames in this case.
|
||||||
lastOutputPresentationTimeUs = presentationTimeUs;
|
latestOutputPresentationTimeUs = presentationTimeUs;
|
||||||
break;
|
break;
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY:
|
case VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY:
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED:
|
case VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED:
|
||||||
lastOutputPresentationTimeUs = presentationTimeUs;
|
latestOutputPresentationTimeUs = presentationTimeUs;
|
||||||
renderFrame(
|
renderFrame(
|
||||||
/* shouldRenderImmediately= */ frameReleaseAction
|
/* shouldRenderImmediately= */ frameReleaseAction
|
||||||
== VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
== VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -184,13 +178,13 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
/** Called when the size of the available frames has changed. */
|
/** Called when the size of the available frames has changed. */
|
||||||
public void onVideoSizeChanged(int width, int height) {
|
public void onVideoSizeChanged(int width, int height) {
|
||||||
videoSizes.add(
|
videoSizes.add(
|
||||||
lastInputPresentationTimeUs == C.TIME_UNSET ? 0 : lastInputPresentationTimeUs + 1,
|
latestInputPresentationTimeUs == C.TIME_UNSET ? 0 : latestInputPresentationTimeUs + 1,
|
||||||
new VideoSize(width, height));
|
new VideoSize(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onStreamStartPositionChanged(long streamStartPositionUs) {
|
public void onStreamStartPositionChanged(long streamStartPositionUs) {
|
||||||
streamStartPositionsUs.add(
|
streamStartPositionsUs.add(
|
||||||
lastInputPresentationTimeUs == C.TIME_UNSET ? 0 : lastInputPresentationTimeUs + 1,
|
latestInputPresentationTimeUs == C.TIME_UNSET ? 0 : latestInputPresentationTimeUs + 1,
|
||||||
streamStartPositionUs);
|
streamStartPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +195,31 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
*/
|
*/
|
||||||
public void onFrameAvailableForRendering(long presentationTimeUs) {
|
public void onFrameAvailableForRendering(long presentationTimeUs) {
|
||||||
presentationTimestampsUs.add(presentationTimeUs);
|
presentationTimestampsUs.add(presentationTimeUs);
|
||||||
lastInputPresentationTimeUs = presentationTimeUs;
|
latestInputPresentationTimeUs = presentationTimeUs;
|
||||||
// TODO b/257464707 - Support extensively modified media.
|
lastPresentationTimeUs = C.TIME_UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals the end of input.
|
||||||
|
*
|
||||||
|
* <p>If a frame becomes {@linkplain #onFrameAvailableForRendering(long) available} after calling
|
||||||
|
* this method, the end of input signal is ignored.
|
||||||
|
*/
|
||||||
|
public void signalEndOfInput() {
|
||||||
|
lastPresentationTimeUs = latestInputPresentationTimeUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether all the frames have been rendered to the output surface.
|
||||||
|
*
|
||||||
|
* <p>This method returns {@code true} if the last frame that became {@linkplain
|
||||||
|
* #onFrameAvailableForRendering(long) available} before {@linkplain #signalEndOfInput()
|
||||||
|
* signalling the end of input} has been rendered, and if no frame has become available in the
|
||||||
|
* mean time.
|
||||||
|
*/
|
||||||
|
public boolean isEnded() {
|
||||||
|
return lastPresentationTimeUs != C.TIME_UNSET
|
||||||
|
&& latestOutputPresentationTimeUs == lastPresentationTimeUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dropFrame() {
|
private void dropFrame() {
|
||||||
|
@ -230,17 +230,17 @@ public class VideoFrameRenderControlTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasReleasedFrame_noFrameReleased_returnsFalse() {
|
public void isEnded_endOfInputNotSignaled_returnsFalse() {
|
||||||
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
VideoFrameRenderControl videoFrameRenderControl =
|
VideoFrameRenderControl videoFrameRenderControl =
|
||||||
new VideoFrameRenderControl(
|
new VideoFrameRenderControl(
|
||||||
mock(VideoFrameRenderControl.FrameRenderer.class), videoFrameReleaseControl);
|
mock(VideoFrameRenderControl.FrameRenderer.class), videoFrameReleaseControl);
|
||||||
|
|
||||||
assertThat(videoFrameRenderControl.hasReleasedFrame(/* presentationTimeUs= */ 0)).isFalse();
|
assertThat(videoFrameRenderControl.isEnded()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasReleasedFrame_frameIsReleased_returnsTrue() throws Exception {
|
public void isEnded_endOfInputSignaled_returnsTrue() throws Exception {
|
||||||
VideoFrameRenderControl.FrameRenderer frameRenderer =
|
VideoFrameRenderControl.FrameRenderer frameRenderer =
|
||||||
mock(VideoFrameRenderControl.FrameRenderer.class);
|
mock(VideoFrameRenderControl.FrameRenderer.class);
|
||||||
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
@ -252,22 +252,13 @@ public class VideoFrameRenderControlTest {
|
|||||||
/* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT);
|
/* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT);
|
||||||
videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 0);
|
videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 0);
|
||||||
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
|
videoFrameRenderControl.signalEndOfInput();
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(frameRenderer);
|
assertThat(videoFrameRenderControl.isEnded()).isTrue();
|
||||||
inOrder
|
|
||||||
.verify(frameRenderer)
|
|
||||||
.onVideoSizeChanged(new VideoSize(/* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT));
|
|
||||||
inOrder
|
|
||||||
.verify(frameRenderer)
|
|
||||||
.renderFrame(
|
|
||||||
/* renderTimeNs= */ anyLong(),
|
|
||||||
/* presentationTimeUs= */ eq(0L),
|
|
||||||
/* isFirstFrame= */ eq(true));
|
|
||||||
assertThat(videoFrameRenderControl.hasReleasedFrame(/* presentationTimeUs= */ 0)).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasReleasedFrame_frameIsReleasedAndFlushed_returnsFalse() throws Exception {
|
public void isEnded_afterFlush_returnsFalse() throws Exception {
|
||||||
VideoFrameRenderControl.FrameRenderer frameRenderer =
|
VideoFrameRenderControl.FrameRenderer frameRenderer =
|
||||||
mock(VideoFrameRenderControl.FrameRenderer.class);
|
mock(VideoFrameRenderControl.FrameRenderer.class);
|
||||||
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
@ -279,21 +270,9 @@ public class VideoFrameRenderControlTest {
|
|||||||
/* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT);
|
/* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT);
|
||||||
videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 0);
|
videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 0);
|
||||||
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(frameRenderer);
|
|
||||||
inOrder
|
|
||||||
.verify(frameRenderer)
|
|
||||||
.onVideoSizeChanged(new VideoSize(/* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT));
|
|
||||||
inOrder
|
|
||||||
.verify(frameRenderer)
|
|
||||||
.renderFrame(
|
|
||||||
/* renderTimeNs= */ anyLong(),
|
|
||||||
/* presentationTimeUs= */ eq(0L),
|
|
||||||
/* isFirstFrame= */ eq(true));
|
|
||||||
|
|
||||||
videoFrameRenderControl.flush();
|
videoFrameRenderControl.flush();
|
||||||
|
|
||||||
assertThat(videoFrameRenderControl.hasReleasedFrame(/* presentationTimeUs= */ 0)).isFalse();
|
assertThat(videoFrameRenderControl.isEnded()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoFrameReleaseControl createVideoFrameReleaseControl() {
|
private static VideoFrameReleaseControl createVideoFrameReleaseControl() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user