Create API enabling use of MediaCodec.BUFFER_FLAG_DECODE_ONLY

If feature is enabled, MediaCodecVideoRenderer will include the `MediaCodec.BUFFER_FLAG_DECODE_ONLY` flag when queuing decode-only input buffers. This flag will signal the decoder to skip the decode-only buffers thereby resulting in faster seeking.

PiperOrigin-RevId: 734152464
This commit is contained in:
michaelkatz 2025-03-06 08:23:21 -08:00 committed by Copybara-Service
parent c030e49dd6
commit 03a0fb4219
3 changed files with 82 additions and 19 deletions

View File

@ -12,6 +12,11 @@
* Allow constant power upmixing/downmixing in DefaultAudioMixer. * Allow constant power upmixing/downmixing in DefaultAudioMixer.
* Add support for float PCM to `ChannelMappingAudioProcessor`. * Add support for float PCM to `ChannelMappingAudioProcessor`.
* Video: * Video:
* Add experimental `ExoPlayer` API to include the
`MediaCodec.BUFFER_FLAG_DECODE_ONLY` flag when queuing decode-only input
buffers. This flag will signal the decoder to skip the decode-only
buffers thereby resulting in faster seeking. Enable it with
`DefaultRenderersFactory.experimentalSetEnableMediaCodecBufferDecodeOnlyFlag`.
* Text: * Text:
* Metadata: * Metadata:
* Image: * Image:

View File

@ -24,10 +24,12 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.audio.AudioRendererEventListener; import androidx.media3.exoplayer.audio.AudioRendererEventListener;
import androidx.media3.exoplayer.audio.AudioSink; import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.DefaultAudioSink; import androidx.media3.exoplayer.audio.DefaultAudioSink;
@ -111,6 +113,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
private boolean enableMediaCodecVideoRendererPrewarming; private boolean enableMediaCodecVideoRendererPrewarming;
private boolean parseAv1SampleDependencies; private boolean parseAv1SampleDependencies;
private long lateThresholdToDropDecoderInputUs; private long lateThresholdToDropDecoderInputUs;
private boolean enableMediaCodecBufferDecodeOnlyFlag;
/** /**
* @param context A {@link Context}. * @param context A {@link Context}.
@ -298,6 +301,26 @@ public class DefaultRenderersFactory implements RenderersFactory {
return this; return this;
} }
/**
* Sets whether the {@link MediaCodec#BUFFER_FLAG_DECODE_ONLY} flag will be included when queuing
* decode-only input buffers to the decoder.
*
* <p>If {@code false}, then only if the decoder is set up in tunneling mode will the decode-only
* input buffers be queued with the {@link MediaCodec#BUFFER_FLAG_DECODE_ONLY} flag. The default
* value is {@code false}.
*
* <p>Requires API 34.
*
* <p>This method is experimental and will be renamed or removed in a future release.
*/
@RequiresApi(34)
@CanIgnoreReturnValue
public DefaultRenderersFactory experimentalSetEnableMediaCodecBufferDecodeOnlyFlag(
boolean enableMediaCodecBufferDecodeOnlyFlag) {
this.enableMediaCodecBufferDecodeOnlyFlag = enableMediaCodecBufferDecodeOnlyFlag;
return this;
}
/** /**
* Sets the maximum duration for which video renderers can attempt to seamlessly join an ongoing * Sets the maximum duration for which video renderers can attempt to seamlessly join an ongoing
* playback. * playback.
@ -406,7 +429,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
VideoRendererEventListener eventListener, VideoRendererEventListener eventListener,
long allowedVideoJoiningTimeMs, long allowedVideoJoiningTimeMs,
ArrayList<Renderer> out) { ArrayList<Renderer> out) {
MediaCodecVideoRenderer videoRenderer = MediaCodecVideoRenderer.Builder videoRendererBuilder =
new MediaCodecVideoRenderer.Builder(context) new MediaCodecVideoRenderer.Builder(context)
.setCodecAdapterFactory(getCodecAdapterFactory()) .setCodecAdapterFactory(getCodecAdapterFactory())
.setMediaCodecSelector(mediaCodecSelector) .setMediaCodecSelector(mediaCodecSelector)
@ -416,9 +439,13 @@ public class DefaultRenderersFactory implements RenderersFactory {
.setEventListener(eventListener) .setEventListener(eventListener)
.setMaxDroppedFramesToNotify(MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY) .setMaxDroppedFramesToNotify(MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)
.experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies) .experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies)
.experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs) .experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs);
.build(); if (Util.SDK_INT >= 34) {
out.add(videoRenderer); videoRendererBuilder =
videoRendererBuilder.experimentalSetEnableMediaCodecBufferDecodeOnlyFlag(
enableMediaCodecBufferDecodeOnlyFlag);
}
out.add(videoRendererBuilder.build());
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) { if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return; return;
@ -851,17 +878,23 @@ public class DefaultRenderersFactory implements RenderersFactory {
long allowedVideoJoiningTimeMs) { long allowedVideoJoiningTimeMs) {
if (enableMediaCodecVideoRendererPrewarming if (enableMediaCodecVideoRendererPrewarming
&& renderer.getClass() == MediaCodecVideoRenderer.class) { && renderer.getClass() == MediaCodecVideoRenderer.class) {
return new MediaCodecVideoRenderer.Builder(context) MediaCodecVideoRenderer.Builder builder =
.setCodecAdapterFactory(getCodecAdapterFactory()) new MediaCodecVideoRenderer.Builder(context)
.setMediaCodecSelector(mediaCodecSelector) .setCodecAdapterFactory(getCodecAdapterFactory())
.setAllowedJoiningTimeMs(allowedVideoJoiningTimeMs) .setMediaCodecSelector(mediaCodecSelector)
.setEnableDecoderFallback(enableDecoderFallback) .setAllowedJoiningTimeMs(allowedVideoJoiningTimeMs)
.setEventHandler(eventHandler) .setEnableDecoderFallback(enableDecoderFallback)
.setEventListener(eventListener) .setEventHandler(eventHandler)
.setMaxDroppedFramesToNotify(MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY) .setEventListener(eventListener)
.experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies) .setMaxDroppedFramesToNotify(MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)
.experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs) .experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies)
.build(); .experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs);
if (Util.SDK_INT >= 34) {
builder =
builder.experimentalSetEnableMediaCodecBufferDecodeOnlyFlag(
enableMediaCodecBufferDecodeOnlyFlag);
}
return builder.build();
} }
return null; return null;
} }

View File

@ -183,6 +183,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
private final long minEarlyUsToDropDecoderInput; private final long minEarlyUsToDropDecoderInput;
private final PriorityQueue<Long> droppedDecoderInputBufferTimestamps; private final PriorityQueue<Long> droppedDecoderInputBufferTimestamps;
private final boolean enableMediaCodecBufferDecodeOnlyFlag;
private @MonotonicNonNull CodecMaxValues codecMaxValues; private @MonotonicNonNull CodecMaxValues codecMaxValues;
private boolean codecNeedsSetOutputSurfaceWorkaround; private boolean codecNeedsSetOutputSurfaceWorkaround;
@ -233,6 +234,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Nullable private VideoSink videoSink; @Nullable private VideoSink videoSink;
private boolean parseAv1SampleDependencies; private boolean parseAv1SampleDependencies;
private long lateThresholdToDropDecoderInputUs; private long lateThresholdToDropDecoderInputUs;
private boolean enableMediaCodecBufferDecodeOnlyFlag;
/** /**
* Creates a new builder. * Creates a new builder.
@ -373,6 +375,26 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
return this; return this;
} }
/**
* Sets whether the {@link MediaCodec#BUFFER_FLAG_DECODE_ONLY} flag will be included when
* queuing decode-only input buffers to the decoder.
*
* <p>If {@code false}, then only if the decoder is set up in tunneling mode will decode-only
* input buffers be queued with the {@link MediaCodec#BUFFER_FLAG_DECODE_ONLY} flag. The default
* value is {@code false}.
*
* <p>Requires API 34.
*
* <p>This method is experimental and will be renamed or removed in a future release.
*/
@RequiresApi(34)
@CanIgnoreReturnValue
public Builder experimentalSetEnableMediaCodecBufferDecodeOnlyFlag(
boolean enableMediaCodecBufferDecodeOnlyFlag) {
this.enableMediaCodecBufferDecodeOnlyFlag = enableMediaCodecBufferDecodeOnlyFlag;
return this;
}
/** /**
* Builds the {@link MediaCodecVideoRenderer}. Must only be called once per Builder instance. * Builds the {@link MediaCodecVideoRenderer}. Must only be called once per Builder instance.
* *
@ -597,6 +619,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
builder.lateThresholdToDropDecoderInputUs != C.TIME_UNSET builder.lateThresholdToDropDecoderInputUs != C.TIME_UNSET
? -builder.lateThresholdToDropDecoderInputUs ? -builder.lateThresholdToDropDecoderInputUs
: C.TIME_UNSET; : C.TIME_UNSET;
enableMediaCodecBufferDecodeOnlyFlag = builder.enableMediaCodecBufferDecodeOnlyFlag;
} }
// FrameTimingEvaluator methods // FrameTimingEvaluator methods
@ -1479,11 +1502,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Override @Override
protected int getCodecBufferFlags(DecoderInputBuffer buffer) { protected int getCodecBufferFlags(DecoderInputBuffer buffer) {
if (Util.SDK_INT >= 34 && tunneling && isBufferBeforeStartTime(buffer)) { if (Util.SDK_INT >= 34
&& (enableMediaCodecBufferDecodeOnlyFlag || tunneling)
&& isBufferBeforeStartTime(buffer)) {
// The buffer likely needs to be dropped because its timestamp is less than the start time. // The buffer likely needs to be dropped because its timestamp is less than the start time.
// We can't decide to do this after decoding because we won't get the buffer back from the // If tunneling, we can't decide to do this after decoding because we won't get the buffer
// codec in tunneling mode. This may not work perfectly, e.g. when the codec is doing frame // back from the codec in tunneling mode. This may not work perfectly, e.g. when the codec is
// rate conversion, but it's still better than not dropping the buffers at all. // doing frame rate conversion, but it's still better than not dropping the buffers at all.
return MediaCodec.BUFFER_FLAG_DECODE_ONLY; return MediaCodec.BUFFER_FLAG_DECODE_ONLY;
} }
return 0; return 0;