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.
* Add support for float PCM to `ChannelMappingAudioProcessor`.
* 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:
* Metadata:
* Image:

View File

@ -24,10 +24,12 @@ import android.os.Handler;
import android.os.Looper;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.C;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.DefaultAudioSink;
@ -111,6 +113,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
private boolean enableMediaCodecVideoRendererPrewarming;
private boolean parseAv1SampleDependencies;
private long lateThresholdToDropDecoderInputUs;
private boolean enableMediaCodecBufferDecodeOnlyFlag;
/**
* @param context A {@link Context}.
@ -298,6 +301,26 @@ public class DefaultRenderersFactory implements RenderersFactory {
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
* playback.
@ -406,7 +429,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
VideoRendererEventListener eventListener,
long allowedVideoJoiningTimeMs,
ArrayList<Renderer> out) {
MediaCodecVideoRenderer videoRenderer =
MediaCodecVideoRenderer.Builder videoRendererBuilder =
new MediaCodecVideoRenderer.Builder(context)
.setCodecAdapterFactory(getCodecAdapterFactory())
.setMediaCodecSelector(mediaCodecSelector)
@ -416,9 +439,13 @@ public class DefaultRenderersFactory implements RenderersFactory {
.setEventListener(eventListener)
.setMaxDroppedFramesToNotify(MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)
.experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies)
.experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs)
.build();
out.add(videoRenderer);
.experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs);
if (Util.SDK_INT >= 34) {
videoRendererBuilder =
videoRendererBuilder.experimentalSetEnableMediaCodecBufferDecodeOnlyFlag(
enableMediaCodecBufferDecodeOnlyFlag);
}
out.add(videoRendererBuilder.build());
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return;
@ -851,17 +878,23 @@ public class DefaultRenderersFactory implements RenderersFactory {
long allowedVideoJoiningTimeMs) {
if (enableMediaCodecVideoRendererPrewarming
&& renderer.getClass() == MediaCodecVideoRenderer.class) {
return new MediaCodecVideoRenderer.Builder(context)
.setCodecAdapterFactory(getCodecAdapterFactory())
.setMediaCodecSelector(mediaCodecSelector)
.setAllowedJoiningTimeMs(allowedVideoJoiningTimeMs)
.setEnableDecoderFallback(enableDecoderFallback)
.setEventHandler(eventHandler)
.setEventListener(eventListener)
.setMaxDroppedFramesToNotify(MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)
.experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies)
.experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs)
.build();
MediaCodecVideoRenderer.Builder builder =
new MediaCodecVideoRenderer.Builder(context)
.setCodecAdapterFactory(getCodecAdapterFactory())
.setMediaCodecSelector(mediaCodecSelector)
.setAllowedJoiningTimeMs(allowedVideoJoiningTimeMs)
.setEnableDecoderFallback(enableDecoderFallback)
.setEventHandler(eventHandler)
.setEventListener(eventListener)
.setMaxDroppedFramesToNotify(MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)
.experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies)
.experimentalSetLateThresholdToDropDecoderInputUs(lateThresholdToDropDecoderInputUs);
if (Util.SDK_INT >= 34) {
builder =
builder.experimentalSetEnableMediaCodecBufferDecodeOnlyFlag(
enableMediaCodecBufferDecodeOnlyFlag);
}
return builder.build();
}
return null;
}

View File

@ -183,6 +183,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
private final long minEarlyUsToDropDecoderInput;
private final PriorityQueue<Long> droppedDecoderInputBufferTimestamps;
private final boolean enableMediaCodecBufferDecodeOnlyFlag;
private @MonotonicNonNull CodecMaxValues codecMaxValues;
private boolean codecNeedsSetOutputSurfaceWorkaround;
@ -233,6 +234,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Nullable private VideoSink videoSink;
private boolean parseAv1SampleDependencies;
private long lateThresholdToDropDecoderInputUs;
private boolean enableMediaCodecBufferDecodeOnlyFlag;
/**
* Creates a new builder.
@ -373,6 +375,26 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
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.
*
@ -597,6 +619,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
builder.lateThresholdToDropDecoderInputUs != C.TIME_UNSET
? -builder.lateThresholdToDropDecoderInputUs
: C.TIME_UNSET;
enableMediaCodecBufferDecodeOnlyFlag = builder.enableMediaCodecBufferDecodeOnlyFlag;
}
// FrameTimingEvaluator methods
@ -1479,11 +1502,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Override
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.
// We can't decide to do this after decoding because we won't get the buffer back from the
// codec in tunneling mode. This may not work perfectly, e.g. when the codec is doing frame
// rate conversion, but it's still better than not dropping the buffers at all.
// If tunneling, we can't decide to do this after decoding because we won't get the buffer
// back from the codec in tunneling mode. This may not work perfectly, e.g. when the codec is
// doing frame rate conversion, but it's still better than not dropping the buffers at all.
return MediaCodec.BUFFER_FLAG_DECODE_ONLY;
}
return 0;