Remove VideoSink.onRendererEnabled

This is part of the effort to make VideoSink independent from renderers.

PiperOrigin-RevId: 740344126
This commit is contained in:
kimvde 2025-03-25 07:42:41 -07:00 committed by Copybara-Service
parent a220b0cb5e
commit 2642d895bd
7 changed files with 154 additions and 113 deletions

View File

@ -79,16 +79,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
videoFrameMetadataListener = (presentationTimeUs, releaseTimeNs, format, mediaFormat) -> {};
}
/**
* {@inheritDoc}
*
* <p>This method will always throw an {@link UnsupportedOperationException}.
*/
@Override
public void onRendererEnabled(boolean mayRenderStartOfStream) {
throw new UnsupportedOperationException();
}
@Override
public void onRendererStarted() {
videoFrameReleaseControl.onStarted();
@ -197,16 +187,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
videoFrameReleaseControl.setChangeFrameRateStrategy(changeFrameRateStrategy);
}
/**
* {@inheritDoc}
*
* <p>This method will always throw an {@link UnsupportedOperationException}.
*/
@Override
public void enableMayRenderStartOfStream() {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*
@ -233,6 +213,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
@Override
public void allowReleaseFirstFrameBeforeStarted() {
videoFrameReleaseControl.allowReleaseFirstFrameBeforeStarted();
}
@Override
public boolean handleInputFrame(
long framePresentationTimeUs, boolean isLastFrame, VideoFrameHandler videoFrameHandler) {

View File

@ -190,6 +190,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
private boolean codecHandlesHdr10PlusOutOfBandMetadata;
private @MonotonicNonNull VideoSink videoSink;
private boolean hasSetVideoSink;
private @VideoSink.FirstFrameReleaseInstruction int nextVideoSinkFirstFrameReleaseInstruction;
private @MonotonicNonNull List<Effect> videoEffects;
@Nullable private Surface displaySurface;
@Nullable private PlaceholderSurface placeholderSurface;
@ -928,7 +929,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
if (videoEffects != null) {
videoSink.setVideoEffects(videoEffects);
}
videoSink.onRendererEnabled(mayRenderStartOfStream);
nextVideoSinkFirstFrameReleaseInstruction =
mayRenderStartOfStream
? RELEASE_FIRST_FRAME_IMMEDIATELY
: RELEASE_FIRST_FRAME_WHEN_STARTED;
@Nullable WakeupListener wakeupListener = getWakeupListener();
if (wakeupListener != null) {
videoSink.setWakeupListener(wakeupListener);
@ -956,7 +960,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Override
public void enableMayRenderStartOfStream() {
if (videoSink != null) {
videoSink.enableMayRenderStartOfStream();
if (nextVideoSinkFirstFrameReleaseInstruction == RELEASE_FIRST_FRAME_IMMEDIATELY
|| nextVideoSinkFirstFrameReleaseInstruction == RELEASE_FIRST_FRAME_WHEN_STARTED) {
// The first stream change hasn't been queued to the sink.
nextVideoSinkFirstFrameReleaseInstruction = RELEASE_FIRST_FRAME_IMMEDIATELY;
} else {
videoSink.allowReleaseFirstFrameBeforeStarted();
}
} else {
videoFrameReleaseControl.allowReleaseFirstFrameBeforeStarted();
}
@ -1642,7 +1652,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
.setWidth(width)
.setHeight(height)
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
.build());
.build(),
nextVideoSinkFirstFrameReleaseInstruction);
nextVideoSinkFirstFrameReleaseInstruction =
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
} else {
videoFrameReleaseControl.setFrameRate(format.frameRate);
}
@ -1656,13 +1669,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
* <p>The default implementation applies this renderer's video effects.
*/
protected void changeVideoSinkInputStream(
VideoSink videoSink, @VideoSink.InputType int inputType, Format format) {
VideoSink videoSink,
@VideoSink.InputType int inputType,
Format format,
@VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction) {
List<Effect> videoEffectsToApply = videoEffects != null ? videoEffects : ImmutableList.of();
videoSink.onInputStreamChanged(
inputType,
format,
getOutputStreamStartPositionUs(),
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
firstFrameReleaseInstruction,
videoEffectsToApply);
}

View File

@ -22,7 +22,6 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.contains;
import static androidx.media3.common.util.Util.getMaxPendingFramesCountForMediaCodecDecoders;
import static androidx.media3.exoplayer.video.VideoSink.INPUT_TYPE_SURFACE;
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.content.Context;
@ -281,12 +280,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
private final Context context;
/**
* A queue of unprocessed input frame start positions. Each position is associated with the
* timestamp from which it should be applied.
*/
private final TimedValueQueue<Long> streamStartPositionsUs;
private final VideoGraph.Factory videoGraphFactory;
private final SparseArray<InputVideoSink> inputVideoSinks;
private final List<Effect> compositionEffects;
@ -297,12 +290,18 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
private final CopyOnWriteArraySet<PlaybackVideoGraphWrapper.Listener> listeners;
private final boolean requestOpenGlToneMapping;
/**
* A queue of unprocessed stream changes. Each stream change is associated with the timestamp from
* which it should be applied.
*/
private TimedValueQueue<StreamChangeInfo> pendingStreamChanges;
private Format videoGraphOutputFormat;
private @MonotonicNonNull HandlerWrapper handler;
private @MonotonicNonNull VideoGraph videoGraph;
private @MonotonicNonNull VideoFrameMetadataListener videoFrameMetadataListener;
private long outputStreamStartPositionUs;
private @VideoSink.FirstFrameReleaseInstruction int nextFirstOutputFrameReleaseInstruction;
private @VideoSink.FirstFrameReleaseInstruction int outputStreamFirstFrameReleaseInstruction;
@Nullable private Pair<Surface, Size> currentSurfaceAndSize;
private int pendingFlushCount;
private @State int state;
@ -331,7 +330,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
private PlaybackVideoGraphWrapper(Builder builder) {
context = builder.context;
streamStartPositionsUs = new TimedValueQueue<>();
pendingStreamChanges = new TimedValueQueue<>();
videoGraphFactory = checkStateNotNull(builder.videoGraphFactory);
inputVideoSinks = new SparseArray<>();
compositionEffects = builder.compositionEffects;
@ -432,13 +431,13 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
// We forward output size changes to the sink even if we are still flushing.
videoGraphOutputFormat =
videoGraphOutputFormat.buildUpon().setWidth(width).setHeight(height).build();
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
onOutputStreamChanged();
}
@Override
public void onOutputFrameRateChanged(float frameRate) {
videoGraphOutputFormat = videoGraphOutputFormat.buildUpon().setFrameRate(frameRate).build();
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
onOutputStreamChanged();
}
@Override
@ -469,13 +468,11 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
// The frame presentation time is relative to the start of the Composition and without the
// renderer offset
lastOutputBufferPresentationTimeUs = bufferPresentationTimeUs;
Long newOutputStreamStartPositionUs =
streamStartPositionsUs.pollFloor(bufferPresentationTimeUs);
if (newOutputStreamStartPositionUs != null
&& newOutputStreamStartPositionUs != outputStreamStartPositionUs) {
outputStreamStartPositionUs = newOutputStreamStartPositionUs;
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
nextFirstOutputFrameReleaseInstruction = RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
StreamChangeInfo streamChangeInfo = pendingStreamChanges.pollFloor(bufferPresentationTimeUs);
if (streamChangeInfo != null) {
outputStreamStartPositionUs = streamChangeInfo.startPositionUs;
outputStreamFirstFrameReleaseInstruction = streamChangeInfo.firstFrameReleaseInstruction;
onOutputStreamChanged();
}
boolean isLastFrame =
finalBufferPresentationTimeUs != C.TIME_UNSET
@ -623,13 +620,15 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
}
pendingFlushCount++;
defaultVideoSink.flush(resetPosition);
while (streamStartPositionsUs.size() > 1) {
streamStartPositionsUs.pollFirst();
while (pendingStreamChanges.size() > 1) {
pendingStreamChanges.pollFirst();
}
if (streamStartPositionsUs.size() == 1) {
// Use the latest startPositionUs if none is passed after flushing.
outputStreamStartPositionUs = checkNotNull(streamStartPositionsUs.pollFirst());
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
if (pendingStreamChanges.size() == 1) {
// Use the latest stream change info if none is passed after flushing.
StreamChangeInfo streamChangeInfo = checkNotNull(pendingStreamChanges.pollFirst());
outputStreamStartPositionUs = streamChangeInfo.startPositionUs;
outputStreamFirstFrameReleaseInstruction = streamChangeInfo.firstFrameReleaseInstruction;
onOutputStreamChanged();
}
lastOutputBufferPresentationTimeUs = C.TIME_UNSET;
finalBufferPresentationTimeUs = C.TIME_UNSET;
@ -667,13 +666,12 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
return inputColorInfo;
}
private void onOutputStreamChanged(
@VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction) {
private void onOutputStreamChanged() {
defaultVideoSink.onInputStreamChanged(
INPUT_TYPE_SURFACE,
videoGraphOutputFormat,
outputStreamStartPositionUs,
firstFrameReleaseInstruction,
outputStreamFirstFrameReleaseInstruction,
/* videoEffects= */ ImmutableList.of());
}
@ -712,14 +710,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
listenerExecutor = NO_OP_EXECUTOR;
}
@Override
public void onRendererEnabled(boolean mayRenderStartOfStream) {
nextFirstOutputFrameReleaseInstruction =
mayRenderStartOfStream
? RELEASE_FIRST_FRAME_IMMEDIATELY
: RELEASE_FIRST_FRAME_WHEN_STARTED;
}
@Override
public void onRendererStarted() {
defaultVideoSink.onRendererStarted();
@ -808,12 +798,8 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
@FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
List<Effect> videoEffects) {
checkState(isInitialized());
switch (inputType) {
case INPUT_TYPE_SURFACE:
case INPUT_TYPE_BITMAP:
break;
default:
throw new UnsupportedOperationException("Unsupported input type " + inputType);
if (inputType != INPUT_TYPE_SURFACE && inputType != INPUT_TYPE_BITMAP) {
throw new UnsupportedOperationException("Unsupported input type " + inputType);
}
setPendingVideoEffects(videoEffects);
this.inputType = inputType;
@ -822,11 +808,56 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
hasSignaledEndOfCurrentInputStream = false;
registerInputStream(format);
// Input timestamps should always be positive because they are offset by ExoPlayer. Adding a
// position to the queue with timestamp 0 should therefore always apply it as long as it is
// the only position in the queue.
streamStartPositionsUs.add(
lastBufferPresentationTimeUs == C.TIME_UNSET ? 0 : lastBufferPresentationTimeUs + 1,
startPositionUs);
// stream change info to the queue with timestamp 0 should therefore always apply it as long
// as it is the only one in the queue.
long fromTimestampUs =
lastBufferPresentationTimeUs == C.TIME_UNSET ? 0 : lastBufferPresentationTimeUs + 1;
pendingStreamChanges.add(
fromTimestampUs,
new StreamChangeInfo(startPositionUs, firstFrameReleaseInstruction, fromTimestampUs));
}
@Override
public void allowReleaseFirstFrameBeforeStarted() {
// We know that this sink is connected to renderers. Each renderer will first queue a stream
// change that has firstFrameReleaseInstruction set to either RELEASE_FIRST_FRAME_IMMEDIATELY
// or RELEASE_FIRST_FRAME_WHEN_STARTED, and then queue stream changes that have
// firstFrameReleaseInstruction set to RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED.
// When a renderer queues the first stream change, all previous streams should have been fully
// processed.
// We want to release the first frame immediately if the firstFrameReleaseInstruction of the
// first stream change queued by the current renderer was RELEASE_FIRST_FRAME_WHEN_STARTED and
// the first frame hasn't been released yet.
if (pendingStreamChanges.size() == 0) {
// All the stream changes have already been processed by the VideoGraph. Delegate to the
// downstream component.
defaultVideoSink.allowReleaseFirstFrameBeforeStarted();
return;
}
TimedValueQueue<StreamChangeInfo> newPendingStreamChanges = new TimedValueQueue<>();
boolean isFirstStreamChange = true;
while (pendingStreamChanges.size() > 0) {
StreamChangeInfo streamChangeInfo = checkNotNull(pendingStreamChanges.pollFirst());
if (isFirstStreamChange) {
if (streamChangeInfo.firstFrameReleaseInstruction == RELEASE_FIRST_FRAME_IMMEDIATELY
|| streamChangeInfo.firstFrameReleaseInstruction
== RELEASE_FIRST_FRAME_WHEN_STARTED) {
// The first stream change hasn't been processed by the VideoGraph yet.
streamChangeInfo =
new StreamChangeInfo(
streamChangeInfo.startPositionUs,
RELEASE_FIRST_FRAME_IMMEDIATELY,
streamChangeInfo.fromTimestampUs);
} else {
// The first stream change has already been processed by the VideoGraph. Delegate to the
// downstream component.
defaultVideoSink.allowReleaseFirstFrameBeforeStarted();
}
isFirstStreamChange = false;
}
newPendingStreamChanges.add(streamChangeInfo.fromTimestampUs, streamChangeInfo);
}
pendingStreamChanges = newPendingStreamChanges;
}
@Override
@ -883,13 +914,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
defaultVideoSink.setChangeFrameRateStrategy(changeFrameRateStrategy);
}
@Override
public void enableMayRenderStartOfStream() {
if (nextFirstOutputFrameReleaseInstruction == RELEASE_FIRST_FRAME_WHEN_STARTED) {
nextFirstOutputFrameReleaseInstruction = RELEASE_FIRST_FRAME_IMMEDIATELY;
}
}
@Override
public boolean handleInputFrame(
long framePresentationTimeUs, boolean isLastFrame, VideoFrameHandler videoFrameHandler) {
@ -1060,6 +1084,21 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
}
}
private static final class StreamChangeInfo {
public final long startPositionUs;
public final @VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction;
public final long fromTimestampUs;
public StreamChangeInfo(
long startPositionUs,
@VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
long fromTimestampUs) {
this.startPositionUs = startPositionUs;
this.firstFrameReleaseInstruction = firstFrameReleaseInstruction;
this.fromTimestampUs = fromTimestampUs;
}
}
/** Delays reflection for loading a {@link VideoGraph.Factory SingleInputVideoGraph} instance. */
private static final class ReflectiveSingleInputVideoGraphFactory implements VideoGraph.Factory {

View File

@ -154,9 +154,6 @@ public interface VideoSink {
*/
int RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED = 2;
/** Called when the {@link Renderer} currently feeding this sink is enabled. */
void onRendererEnabled(boolean mayRenderStartOfStream);
/** Called when the {@link Renderer} currently feeding this sink is started. */
void onRendererStarted();
@ -261,15 +258,6 @@ public interface VideoSink {
*/
void setChangeFrameRateStrategy(@C.VideoChangeFrameRateStrategy int changeFrameRateStrategy);
/**
* Enables this video sink to render the start of the stream to its output surface even if the
* renderer is not {@linkplain #onRendererStarted() started} yet.
*
* <p>This is used to update the value of {@code mayRenderStartOfStream} passed to {@link
* #onRendererEnabled(boolean)}.
*/
void enableMayRenderStartOfStream();
/**
* Informs the video sink that a new input stream will be queued with the given effects.
*
@ -290,6 +278,15 @@ public interface VideoSink {
@FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
List<Effect> videoEffects);
/**
* Allows the sink to release the first frame even if rendering is not {@linkplain
* #onRendererStarted() started}.
*
* <p>This is used to update the {@link FirstFrameReleaseInstruction} of the {@linkplain
* #onInputStreamChanged(int, Format, long, int, List) stream} that is currently being processed.
*/
void allowReleaseFirstFrameBeforeStarted();
/**
* Handles a video input frame.
*

View File

@ -81,11 +81,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
pendingOperations.clear();
}
@Override
public void onRendererEnabled(boolean mayRenderStartOfStream) {
executeOrDelay(videoSink -> videoSink.onRendererEnabled(mayRenderStartOfStream));
}
@Override
public void onRendererStarted() {
executeOrDelay(VideoSink::onRendererStarted);
@ -213,11 +208,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
executeOrDelay(videoSink -> videoSink.setChangeFrameRateStrategy(changeFrameRateStrategy));
}
@Override
public void enableMayRenderStartOfStream() {
executeOrDelay(VideoSink::enableMayRenderStartOfStream);
}
@Override
public void onInputStreamChanged(
@InputType int inputType,
@ -231,6 +221,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
inputType, format, startPositionUs, firstFrameReleaseInstruction, videoEffects));
}
@Override
public void allowReleaseFirstFrameBeforeStarted() {
executeOrDelay(VideoSink::allowReleaseFirstFrameBeforeStarted);
}
/**
* {@inheritDoc}
*

View File

@ -24,7 +24,9 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.SDK_INT;
import static androidx.media3.exoplayer.DefaultRenderersFactory.DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS;
import static androidx.media3.exoplayer.DefaultRenderersFactory.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY;
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_IMMEDIATELY;
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_STARTED;
import android.content.Context;
import android.graphics.Bitmap;
@ -435,12 +437,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
protected void changeVideoSinkInputStream(
VideoSink videoSink, @VideoSink.InputType int inputType, Format format) {
VideoSink videoSink,
@VideoSink.InputType int inputType,
Format format,
@VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction) {
videoSink.onInputStreamChanged(
inputType,
format,
getOutputStreamStartPositionUs(),
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
firstFrameReleaseInstruction,
pendingEffects);
}
@ -493,6 +498,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private boolean inputStreamPending;
private long streamStartPositionUs;
private boolean mayRenderStartOfStream;
private @VideoSink.FirstFrameReleaseInstruction int nextFirstFrameReleaseInstruction;
private long offsetToCompositionTimeUs;
public SequenceImageRenderer(
@ -513,7 +519,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
throws ExoPlaybackException {
super.onEnabled(joining, mayRenderStartOfStream);
this.mayRenderStartOfStream = mayRenderStartOfStream;
videoSink.onRendererEnabled(mayRenderStartOfStream);
nextFirstFrameReleaseInstruction =
mayRenderStartOfStream
? RELEASE_FIRST_FRAME_IMMEDIATELY
: RELEASE_FIRST_FRAME_WHEN_STARTED;
// TODO: b/328444280 - Do not set a listener on VideoSink, but MediaCodecVideoRenderer must
// unregister itself as a listener too.
videoSink.setListener(VideoSink.Listener.NO_OP, /* executor= */ (runnable) -> {});
@ -630,8 +639,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
.setFrameRate(/* frameRate= */ DEFAULT_FRAME_RATE)
.build(),
streamStartPositionUs,
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
nextFirstFrameReleaseInstruction,
videoEffects);
nextFirstFrameReleaseInstruction = RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
inputStreamPending = false;
}
if (!videoSink.handleInputBitmap(outputImage, checkStateNotNull(timestampIterator))) {

View File

@ -40,26 +40,25 @@ public class BufferingVideoSinkTest {
VideoSink videoSinkMock = mock(VideoSink.class);
bufferingVideoSink.setVideoSink(videoSinkMock);
bufferingVideoSink.onRendererEnabled(/* mayRenderStartOfStream= */ true);
bufferingVideoSink.onRendererStarted();
bufferingVideoSink.flush(/* resetPosition= */ true);
InOrder inOrder = Mockito.inOrder(videoSinkMock);
inOrder.verify(videoSinkMock).onRendererEnabled(/* mayRenderStartOfStream= */ true);
inOrder.verify(videoSinkMock).onRendererStarted();
inOrder.verify(videoSinkMock).flush(/* resetPosition= */ true);
}
@Test
public void setVideoSink_executesPendingOperations() {
BufferingVideoSink bufferingVideoSink = new BufferingVideoSink(context);
VideoSink videoSinkMock = mock(VideoSink.class);
bufferingVideoSink.onRendererEnabled(/* mayRenderStartOfStream= */ true);
bufferingVideoSink.onRendererStarted();
bufferingVideoSink.flush(/* resetPosition= */ true);
bufferingVideoSink.setVideoSink(videoSinkMock);
InOrder inOrder = Mockito.inOrder(videoSinkMock);
inOrder.verify(videoSinkMock).onRendererEnabled(/* mayRenderStartOfStream= */ true);
inOrder.verify(videoSinkMock).onRendererStarted();
inOrder.verify(videoSinkMock).flush(/* resetPosition= */ true);
}
@Test
@ -69,11 +68,11 @@ public class BufferingVideoSinkTest {
bufferingVideoSink.setVideoSink(videoSinkMock);
bufferingVideoSink.setVideoSink(null);
bufferingVideoSink.onRendererEnabled(/* mayRenderStartOfStream= */ true);
bufferingVideoSink.onRendererStarted();
bufferingVideoSink.flush(/* resetPosition= */ true);
verify(videoSinkMock, never()).onRendererEnabled(/* mayRenderStartOfStream= */ true);
verify(videoSinkMock, never()).onRendererStarted();
verify(videoSinkMock, never()).flush(/* resetPosition= */ true);
}
@Test
@ -81,12 +80,12 @@ public class BufferingVideoSinkTest {
BufferingVideoSink bufferingVideoSink = new BufferingVideoSink(context);
VideoSink videoSinkMock = mock(VideoSink.class);
bufferingVideoSink.onRendererEnabled(/* mayRenderStartOfStream= */ true);
bufferingVideoSink.onRendererStarted();
bufferingVideoSink.flush(/* resetPosition= */ true);
bufferingVideoSink.clearPendingOperations();
bufferingVideoSink.setVideoSink(videoSinkMock);
verify(videoSinkMock, never()).onRendererEnabled(/* mayRenderStartOfStream= */ true);
verify(videoSinkMock, never()).onRendererStarted();
verify(videoSinkMock, never()).flush(/* resetPosition= */ true);
}
}