mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Remove implementation of DefaultVideoSink.onRendererEnabled
The goal is to get rid of VideoSink.onRendererEnabled so that this interface becomes renderer-agnostic. Indeed, for multi video sequences, the DefaultVideoSink won't receive input from a single renderer anymore. This is a no-op refactoring PiperOrigin-RevId: 738296515
This commit is contained in:
parent
d0d76f214a
commit
14c06eaf8e
@ -17,9 +17,6 @@ package androidx.media3.exoplayer.video;
|
|||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY;
|
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_STARTED;
|
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
@ -82,11 +79,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
videoFrameMetadataListener = (presentationTimeUs, releaseTimeNs, format, mediaFormat) -> {};
|
videoFrameMetadataListener = (presentationTimeUs, releaseTimeNs, format, mediaFormat) -> {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>This method will always throw an {@link UnsupportedOperationException}.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onRendererEnabled(boolean mayRenderStartOfStream) {
|
public void onRendererEnabled(boolean mayRenderStartOfStream) {
|
||||||
int firstFrameReleaseInstruction =
|
throw new UnsupportedOperationException();
|
||||||
mayRenderStartOfStream ? RELEASE_FIRST_FRAME_IMMEDIATELY : RELEASE_FIRST_FRAME_WHEN_STARTED;
|
|
||||||
videoFrameRenderControl.onStreamChanged(firstFrameReleaseInstruction, streamStartPositionUs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -197,9 +197,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
videoFrameReleaseControl.setChangeFrameRateStrategy(changeFrameRateStrategy);
|
videoFrameReleaseControl.setChangeFrameRateStrategy(changeFrameRateStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>This method will always throw an {@link UnsupportedOperationException}.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void enableMayRenderStartOfStream() {
|
public void enableMayRenderStartOfStream() {
|
||||||
videoFrameReleaseControl.allowReleaseFirstFrameBeforeStarted();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,7 +214,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onInputStreamChanged(
|
public void onInputStreamChanged(
|
||||||
@InputType int inputType, Format format, long startPositionUs, List<Effect> videoEffects) {
|
@InputType int inputType,
|
||||||
|
Format format,
|
||||||
|
long startPositionUs,
|
||||||
|
@FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
|
||||||
|
List<Effect> videoEffects) {
|
||||||
checkState(videoEffects.isEmpty());
|
checkState(videoEffects.isEmpty());
|
||||||
if (format.width != inputFormat.width || format.height != inputFormat.height) {
|
if (format.width != inputFormat.width || format.height != inputFormat.height) {
|
||||||
videoFrameRenderControl.onVideoSizeChanged(format.width, format.height);
|
videoFrameRenderControl.onVideoSizeChanged(format.width, format.height);
|
||||||
@ -219,8 +228,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
inputFormat = format;
|
inputFormat = format;
|
||||||
if (startPositionUs != this.streamStartPositionUs) {
|
if (startPositionUs != this.streamStartPositionUs) {
|
||||||
videoFrameRenderControl.onStreamChanged(
|
videoFrameRenderControl.onStreamChanged(firstFrameReleaseInstruction, startPositionUs);
|
||||||
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED, startPositionUs);
|
|
||||||
this.streamStartPositionUs = startPositionUs;
|
this.streamStartPositionUs = startPositionUs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,9 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
|||||||
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_MAX_INPUT_SIZE_EXCEEDED;
|
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_MAX_INPUT_SIZE_EXCEEDED;
|
||||||
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_VIDEO_MAX_RESOLUTION_EXCEEDED;
|
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_VIDEO_MAX_RESOLUTION_EXCEEDED;
|
||||||
import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_NO;
|
import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_NO;
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_IMMEDIATELY;
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_STARTED;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_STARTED;
|
||||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
@ -1650,7 +1650,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when ready to {@linkplain VideoSink#onInputStreamChanged(int, Format, long, List)
|
* Called when ready to {@linkplain VideoSink#onInputStreamChanged(int, Format, long, int, List)
|
||||||
* change} the input stream.
|
* change} the input stream.
|
||||||
*
|
*
|
||||||
* <p>The default implementation applies this renderer's video effects.
|
* <p>The default implementation applies this renderer's video effects.
|
||||||
@ -1659,7 +1659,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
VideoSink videoSink, @VideoSink.InputType int inputType, Format format) {
|
VideoSink videoSink, @VideoSink.InputType int inputType, Format format) {
|
||||||
List<Effect> videoEffectsToApply = videoEffects != null ? videoEffects : ImmutableList.of();
|
List<Effect> videoEffectsToApply = videoEffects != null ? videoEffects : ImmutableList.of();
|
||||||
videoSink.onInputStreamChanged(
|
videoSink.onInputStreamChanged(
|
||||||
inputType, format, getOutputStreamStartPositionUs(), videoEffectsToApply);
|
inputType,
|
||||||
|
format,
|
||||||
|
getOutputStreamStartPositionUs(),
|
||||||
|
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
|
||||||
|
videoEffectsToApply);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,6 +22,7 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
|||||||
import static androidx.media3.common.util.Util.contains;
|
import static androidx.media3.common.util.Util.contains;
|
||||||
import static androidx.media3.common.util.Util.getMaxPendingFramesCountForMediaCodecDecoders;
|
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.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 static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -297,6 +298,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
private @MonotonicNonNull HandlerWrapper handler;
|
private @MonotonicNonNull HandlerWrapper handler;
|
||||||
private @MonotonicNonNull VideoGraph videoGraph;
|
private @MonotonicNonNull VideoGraph videoGraph;
|
||||||
private long outputStreamStartPositionUs;
|
private long outputStreamStartPositionUs;
|
||||||
|
private @VideoSink.FirstFrameReleaseInstruction int nextFirstOutputFrameReleaseInstruction;
|
||||||
@Nullable private Pair<Surface, Size> currentSurfaceAndSize;
|
@Nullable private Pair<Surface, Size> currentSurfaceAndSize;
|
||||||
private int pendingFlushCount;
|
private int pendingFlushCount;
|
||||||
private @State int state;
|
private @State int state;
|
||||||
@ -347,6 +349,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
listeners = new CopyOnWriteArraySet<>();
|
listeners = new CopyOnWriteArraySet<>();
|
||||||
requestOpenGlToneMapping = builder.requestOpenGlToneMapping;
|
requestOpenGlToneMapping = builder.requestOpenGlToneMapping;
|
||||||
videoGraphOutputFormat = new Format.Builder().build();
|
videoGraphOutputFormat = new Format.Builder().build();
|
||||||
|
outputStreamStartPositionUs = C.TIME_UNSET;
|
||||||
lastOutputBufferPresentationTimeUs = C.TIME_UNSET;
|
lastOutputBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
totalVideoInputCount = C.LENGTH_UNSET;
|
totalVideoInputCount = C.LENGTH_UNSET;
|
||||||
@ -425,13 +428,13 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
// We forward output size changes to the sink even if we are still flushing.
|
// We forward output size changes to the sink even if we are still flushing.
|
||||||
videoGraphOutputFormat =
|
videoGraphOutputFormat =
|
||||||
videoGraphOutputFormat.buildUpon().setWidth(width).setHeight(height).build();
|
videoGraphOutputFormat.buildUpon().setWidth(width).setHeight(height).build();
|
||||||
onOutputStreamChanged();
|
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOutputFrameRateChanged(float frameRate) {
|
public void onOutputFrameRateChanged(float frameRate) {
|
||||||
videoGraphOutputFormat = videoGraphOutputFormat.buildUpon().setFrameRate(frameRate).build();
|
videoGraphOutputFormat = videoGraphOutputFormat.buildUpon().setFrameRate(frameRate).build();
|
||||||
onOutputStreamChanged();
|
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -453,7 +456,8 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
if (newOutputStreamStartPositionUs != null
|
if (newOutputStreamStartPositionUs != null
|
||||||
&& newOutputStreamStartPositionUs != outputStreamStartPositionUs) {
|
&& newOutputStreamStartPositionUs != outputStreamStartPositionUs) {
|
||||||
outputStreamStartPositionUs = newOutputStreamStartPositionUs;
|
outputStreamStartPositionUs = newOutputStreamStartPositionUs;
|
||||||
onOutputStreamChanged();
|
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
|
||||||
|
nextFirstOutputFrameReleaseInstruction = RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
||||||
}
|
}
|
||||||
boolean isLastFrame =
|
boolean isLastFrame =
|
||||||
finalBufferPresentationTimeUs != C.TIME_UNSET
|
finalBufferPresentationTimeUs != C.TIME_UNSET
|
||||||
@ -598,7 +602,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
if (streamStartPositionsUs.size() == 1) {
|
if (streamStartPositionsUs.size() == 1) {
|
||||||
// Use the latest startPositionUs if none is passed after flushing.
|
// Use the latest startPositionUs if none is passed after flushing.
|
||||||
outputStreamStartPositionUs = checkNotNull(streamStartPositionsUs.pollFirst());
|
outputStreamStartPositionUs = checkNotNull(streamStartPositionsUs.pollFirst());
|
||||||
onOutputStreamChanged();
|
onOutputStreamChanged(nextFirstOutputFrameReleaseInstruction);
|
||||||
}
|
}
|
||||||
lastOutputBufferPresentationTimeUs = C.TIME_UNSET;
|
lastOutputBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
@ -635,11 +639,13 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
return inputColorInfo;
|
return inputColorInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onOutputStreamChanged() {
|
private void onOutputStreamChanged(
|
||||||
|
@VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction) {
|
||||||
defaultVideoSink.onInputStreamChanged(
|
defaultVideoSink.onInputStreamChanged(
|
||||||
INPUT_TYPE_SURFACE,
|
INPUT_TYPE_SURFACE,
|
||||||
videoGraphOutputFormat,
|
videoGraphOutputFormat,
|
||||||
outputStreamStartPositionUs,
|
outputStreamStartPositionUs,
|
||||||
|
firstFrameReleaseInstruction,
|
||||||
/* videoEffects= */ ImmutableList.of());
|
/* videoEffects= */ ImmutableList.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,7 +686,10 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRendererEnabled(boolean mayRenderStartOfStream) {
|
public void onRendererEnabled(boolean mayRenderStartOfStream) {
|
||||||
defaultVideoSink.onRendererEnabled(mayRenderStartOfStream);
|
nextFirstOutputFrameReleaseInstruction =
|
||||||
|
mayRenderStartOfStream
|
||||||
|
? RELEASE_FIRST_FRAME_IMMEDIATELY
|
||||||
|
: RELEASE_FIRST_FRAME_WHEN_STARTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -765,7 +774,11 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInputStreamChanged(
|
public void onInputStreamChanged(
|
||||||
@InputType int inputType, Format format, long startPositionUs, List<Effect> videoEffects) {
|
@InputType int inputType,
|
||||||
|
Format format,
|
||||||
|
long startPositionUs,
|
||||||
|
@FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
|
||||||
|
List<Effect> videoEffects) {
|
||||||
checkState(isInitialized());
|
checkState(isInitialized());
|
||||||
switch (inputType) {
|
switch (inputType) {
|
||||||
case INPUT_TYPE_SURFACE:
|
case INPUT_TYPE_SURFACE:
|
||||||
@ -844,7 +857,9 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableMayRenderStartOfStream() {
|
public void enableMayRenderStartOfStream() {
|
||||||
defaultVideoSink.enableMayRenderStartOfStream();
|
if (nextFirstOutputFrameReleaseInstruction == RELEASE_FIRST_FRAME_WHEN_STARTED) {
|
||||||
|
nextFirstOutputFrameReleaseInstruction = RELEASE_FIRST_FRAME_IMMEDIATELY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,6 +17,9 @@ package androidx.media3.exoplayer.video;
|
|||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
import static androidx.media3.common.util.Util.msToUs;
|
import static androidx.media3.common.util.Util.msToUs;
|
||||||
|
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 static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
@ -40,35 +43,6 @@ import java.lang.annotation.Target;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class VideoFrameReleaseControl {
|
public final class VideoFrameReleaseControl {
|
||||||
|
|
||||||
/**
|
|
||||||
* The instruction provided to {@link #onStreamChanged(int)} for releasing the first frame.
|
|
||||||
*
|
|
||||||
* <p>One of {@link #RELEASE_FIRST_FRAME_IMMEDIATELY}, {@link #RELEASE_FIRST_FRAME_WHEN_STARTED}
|
|
||||||
* or {@link #RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED}.
|
|
||||||
*/
|
|
||||||
@Documented
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
@Target(TYPE_USE)
|
|
||||||
@UnstableApi
|
|
||||||
@IntDef({
|
|
||||||
RELEASE_FIRST_FRAME_IMMEDIATELY,
|
|
||||||
RELEASE_FIRST_FRAME_WHEN_STARTED,
|
|
||||||
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED
|
|
||||||
})
|
|
||||||
public @interface FirstFrameReleaseInstruction {}
|
|
||||||
|
|
||||||
/** Instructs to release the first frame as soon as possible. */
|
|
||||||
public static final int RELEASE_FIRST_FRAME_IMMEDIATELY = 0;
|
|
||||||
|
|
||||||
/** Instructs to release the first frame when rendering starts. */
|
|
||||||
public static final int RELEASE_FIRST_FRAME_WHEN_STARTED = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs to release the first frame when the playback position reaches the stream start
|
|
||||||
* position.
|
|
||||||
*/
|
|
||||||
public static final int RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED = 2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The frame release action returned by {@link #getFrameReleaseAction(long, long, long, long,
|
* The frame release action returned by {@link #getFrameReleaseAction(long, long, long, long,
|
||||||
* boolean, boolean, FrameReleaseInfo)}.
|
* boolean, boolean, FrameReleaseInfo)}.
|
||||||
@ -241,7 +215,8 @@ public final class VideoFrameReleaseControl {
|
|||||||
*
|
*
|
||||||
* <p>Must also be called for the first stream.
|
* <p>Must also be called for the first stream.
|
||||||
*/
|
*/
|
||||||
public void onStreamChanged(@FirstFrameReleaseInstruction int firstFrameReleaseInstruction) {
|
public void onStreamChanged(
|
||||||
|
@VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction) {
|
||||||
switch (firstFrameReleaseInstruction) {
|
switch (firstFrameReleaseInstruction) {
|
||||||
case RELEASE_FIRST_FRAME_IMMEDIATELY:
|
case RELEASE_FIRST_FRAME_IMMEDIATELY:
|
||||||
firstFrameState = C.FIRST_FRAME_NOT_RENDERED;
|
firstFrameState = C.FIRST_FRAME_NOT_RENDERED;
|
||||||
|
@ -17,7 +17,7 @@ package androidx.media3.exoplayer.video;
|
|||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
@ -179,7 +179,7 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onStreamChanged(
|
public void onStreamChanged(
|
||||||
@VideoFrameReleaseControl.FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
|
@VideoSink.FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
|
||||||
long streamStartPositionUs) {
|
long streamStartPositionUs) {
|
||||||
if (presentationTimestampsUs.isEmpty()) {
|
if (presentationTimestampsUs.isEmpty()) {
|
||||||
videoFrameReleaseControl.onStreamChanged(firstFrameReleaseInstruction);
|
videoFrameReleaseControl.onStreamChanged(firstFrameReleaseInstruction);
|
||||||
|
@ -125,6 +125,35 @@ public interface VideoSink {
|
|||||||
/** Input frames come from a {@link Bitmap}. */
|
/** Input frames come from a {@link Bitmap}. */
|
||||||
int INPUT_TYPE_BITMAP = 2;
|
int INPUT_TYPE_BITMAP = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instruction provided when the stream changes for releasing the first frame.
|
||||||
|
*
|
||||||
|
* <p>One of {@link #RELEASE_FIRST_FRAME_IMMEDIATELY}, {@link #RELEASE_FIRST_FRAME_WHEN_STARTED}
|
||||||
|
* or {@link #RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED}.
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@Target(TYPE_USE)
|
||||||
|
@UnstableApi
|
||||||
|
@IntDef({
|
||||||
|
RELEASE_FIRST_FRAME_IMMEDIATELY,
|
||||||
|
RELEASE_FIRST_FRAME_WHEN_STARTED,
|
||||||
|
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED
|
||||||
|
})
|
||||||
|
@interface FirstFrameReleaseInstruction {}
|
||||||
|
|
||||||
|
/** Instructs to release the first frame as soon as possible. */
|
||||||
|
int RELEASE_FIRST_FRAME_IMMEDIATELY = 0;
|
||||||
|
|
||||||
|
/** Instructs to release the first frame when rendering starts. */
|
||||||
|
int RELEASE_FIRST_FRAME_WHEN_STARTED = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs to release the first frame when the playback position reaches the stream start
|
||||||
|
* position.
|
||||||
|
*/
|
||||||
|
int RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED = 2;
|
||||||
|
|
||||||
/** Called when the {@link Renderer} currently feeding this sink is enabled. */
|
/** Called when the {@link Renderer} currently feeding this sink is enabled. */
|
||||||
void onRendererEnabled(boolean mayRenderStartOfStream);
|
void onRendererEnabled(boolean mayRenderStartOfStream);
|
||||||
|
|
||||||
@ -188,7 +217,7 @@ public interface VideoSink {
|
|||||||
*
|
*
|
||||||
* <p>This method returns {@code true} if the end of the last input stream has been {@linkplain
|
* <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
|
* #signalEndOfCurrentInputStream() signaled} and all the input frames have been rendered. Note
|
||||||
* that a new input stream can be {@linkplain #onInputStreamChanged(int, Format, long, List)
|
* that a new input stream can be {@linkplain #onInputStreamChanged(int, Format, long, int, List)
|
||||||
* signaled} even when this method returns true (in which case the sink will not be ended
|
* signaled} even when this method returns true (in which case the sink will not be ended
|
||||||
* anymore).
|
* anymore).
|
||||||
*/
|
*/
|
||||||
@ -250,16 +279,22 @@ public interface VideoSink {
|
|||||||
* @param format The {@link Format} of the stream.
|
* @param format The {@link Format} of the stream.
|
||||||
* @param startPositionUs The start position of the buffer presentation timestamps of the stream,
|
* @param startPositionUs The start position of the buffer presentation timestamps of the stream,
|
||||||
* in microseconds.
|
* in microseconds.
|
||||||
|
* @param firstFrameReleaseInstruction The {@link FirstFrameReleaseInstruction} indicating when to
|
||||||
|
* release the stream's first frame.
|
||||||
* @param videoEffects The {@link List<Effect>} to apply to the new stream.
|
* @param videoEffects The {@link List<Effect>} to apply to the new stream.
|
||||||
*/
|
*/
|
||||||
void onInputStreamChanged(
|
void onInputStreamChanged(
|
||||||
@InputType int inputType, Format format, long startPositionUs, List<Effect> videoEffects);
|
@InputType int inputType,
|
||||||
|
Format format,
|
||||||
|
long startPositionUs,
|
||||||
|
@FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
|
||||||
|
List<Effect> videoEffects);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a video input frame.
|
* Handles a video input frame.
|
||||||
*
|
*
|
||||||
* <p>Must be called after the corresponding stream is {@linkplain #onInputStreamChanged(int,
|
* <p>Must be called after the corresponding stream is {@linkplain #onInputStreamChanged(int,
|
||||||
* Format, long, List) signaled}.
|
* Format, long, int, List) signaled}.
|
||||||
*
|
*
|
||||||
* @param framePresentationTimeUs The frame's presentation time, in microseconds.
|
* @param framePresentationTimeUs The frame's presentation time, in microseconds.
|
||||||
* @param isLastFrame Whether this is the last frame of the video stream. This flag is set on a
|
* @param isLastFrame Whether this is the last frame of the video stream. This flag is set on a
|
||||||
@ -276,7 +311,7 @@ public interface VideoSink {
|
|||||||
* Handles an input {@link Bitmap}.
|
* Handles an input {@link Bitmap}.
|
||||||
*
|
*
|
||||||
* <p>Must be called after the corresponding stream is {@linkplain #onInputStreamChanged(int,
|
* <p>Must be called after the corresponding stream is {@linkplain #onInputStreamChanged(int,
|
||||||
* Format, long, List) signaled}.
|
* Format, long, int, List) signaled}.
|
||||||
*
|
*
|
||||||
* @param inputBitmap The {@link Bitmap} to queue to the video sink.
|
* @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
|
* @param timestampIterator The times within the current stream that the bitmap should be shown
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.video;
|
package androidx.media3.exoplayer.video;
|
||||||
|
|
||||||
|
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 com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@ -80,10 +82,24 @@ public final class PlaybackVideoGraphWrapperTest {
|
|||||||
|
|
||||||
sink.initialize(format);
|
sink.initialize(format);
|
||||||
|
|
||||||
sink.onInputStreamChanged(VideoSink.INPUT_TYPE_SURFACE, format, startPositionUs, firstEffects);
|
|
||||||
sink.onInputStreamChanged(VideoSink.INPUT_TYPE_SURFACE, format, startPositionUs, secondEffects);
|
|
||||||
sink.onInputStreamChanged(
|
sink.onInputStreamChanged(
|
||||||
VideoSink.INPUT_TYPE_SURFACE, format, startPositionUs, ImmutableList.of());
|
VideoSink.INPUT_TYPE_SURFACE,
|
||||||
|
format,
|
||||||
|
startPositionUs,
|
||||||
|
RELEASE_FIRST_FRAME_IMMEDIATELY,
|
||||||
|
firstEffects);
|
||||||
|
sink.onInputStreamChanged(
|
||||||
|
VideoSink.INPUT_TYPE_SURFACE,
|
||||||
|
format,
|
||||||
|
startPositionUs,
|
||||||
|
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
|
||||||
|
secondEffects);
|
||||||
|
sink.onInputStreamChanged(
|
||||||
|
VideoSink.INPUT_TYPE_SURFACE,
|
||||||
|
format,
|
||||||
|
startPositionUs,
|
||||||
|
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
|
||||||
|
ImmutableList.of());
|
||||||
testVideoGraphFactory.verifyRegisteredEffectsMatches(/* invocationTimes= */ 3);
|
testVideoGraphFactory.verifyRegisteredEffectsMatches(/* invocationTimes= */ 3);
|
||||||
assertThat(testVideoGraphFactory.getCapturedEffects())
|
assertThat(testVideoGraphFactory.getCapturedEffects())
|
||||||
.isEqualTo(ImmutableList.of(firstEffects, secondEffects, ImmutableList.of()));
|
.isEqualTo(ImmutableList.of(firstEffects, secondEffects, ImmutableList.of()));
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.video;
|
package androidx.media3.exoplayer.video;
|
||||||
|
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_IMMEDIATELY;
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_STARTED;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_STARTED;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
@ -587,7 +587,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* isDecodeOnlyFrame= */ true,
|
/* isDecodeOnlyFrame= */ true,
|
||||||
/* isLastFrame= */ true,
|
/* isLastFrame= */ true,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY);
|
.isEqualTo(RELEASE_FIRST_FRAME_IMMEDIATELY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.video;
|
package androidx.media3.exoplayer.video;
|
||||||
|
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_IMMEDIATELY;
|
||||||
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
@ -220,10 +220,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInputStreamChanged(
|
public void onInputStreamChanged(
|
||||||
@InputType int inputType, Format format, long startPositionUs, List<Effect> videoEffects) {
|
@InputType int inputType,
|
||||||
|
Format format,
|
||||||
|
long startPositionUs,
|
||||||
|
@FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
|
||||||
|
List<Effect> videoEffects) {
|
||||||
executeOrDelay(
|
executeOrDelay(
|
||||||
videoSink ->
|
videoSink ->
|
||||||
videoSink.onInputStreamChanged(inputType, format, startPositionUs, videoEffects));
|
videoSink.onInputStreamChanged(
|
||||||
|
inputType, format, startPositionUs, firstFrameReleaseInstruction, videoEffects));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +24,7 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
|||||||
import static androidx.media3.common.util.Util.SDK_INT;
|
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.DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS;
|
||||||
import static androidx.media3.exoplayer.DefaultRenderersFactory.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY;
|
import static androidx.media3.exoplayer.DefaultRenderersFactory.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY;
|
||||||
|
import static androidx.media3.exoplayer.video.VideoSink.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -436,7 +437,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
protected void changeVideoSinkInputStream(
|
protected void changeVideoSinkInputStream(
|
||||||
VideoSink videoSink, @VideoSink.InputType int inputType, Format format) {
|
VideoSink videoSink, @VideoSink.InputType int inputType, Format format) {
|
||||||
videoSink.onInputStreamChanged(
|
videoSink.onInputStreamChanged(
|
||||||
inputType, format, getOutputStreamStartPositionUs(), pendingEffects);
|
inputType,
|
||||||
|
format,
|
||||||
|
getOutputStreamStartPositionUs(),
|
||||||
|
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
|
||||||
|
pendingEffects);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void activateBufferingVideoSink() {
|
private void activateBufferingVideoSink() {
|
||||||
@ -625,6 +630,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
.setFrameRate(/* frameRate= */ DEFAULT_FRAME_RATE)
|
.setFrameRate(/* frameRate= */ DEFAULT_FRAME_RATE)
|
||||||
.build(),
|
.build(),
|
||||||
streamStartPositionUs,
|
streamStartPositionUs,
|
||||||
|
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED,
|
||||||
videoEffects);
|
videoEffects);
|
||||||
inputStreamPending = false;
|
inputStreamPending = false;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user