diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6909025ec1..0963e9142c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,6 +4,12 @@ * Common Library: * ExoPlayer: + * Add experimental 'ExoPlayer' pre-warming support for playback using + `MediaCodecVideoRenderer`. `DefaultRenderersFactory` can be configured + through `experimentalSetEnableMediaCodecVideoRendererPrewarming` to + provide a secondary `MediaCodecVideoRenderer` to `ExoPlayer`. If + enabled, `ExoPlayer` will pre-process the video of consecutive media + items during playback to reduce media item transition latency. * Transformer: * Enable support for Android platform diagnostics via `MediaMetricsManager`. Transformer will forward editing events and diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index bb2e9a9dd6..68e7103c36 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.Looper; import androidx.annotation.IntDef; import androidx.annotation.Nullable; +import androidx.media3.common.C; import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.audio.AudioRendererEventListener; @@ -106,6 +107,7 @@ public class DefaultRenderersFactory implements RenderersFactory { private MediaCodecSelector mediaCodecSelector; private boolean enableFloatOutput; private boolean enableAudioTrackPlaybackParams; + private boolean enableMediaCodecVideoRendererPrewarming; /** * @param context A {@link Context}. @@ -248,6 +250,32 @@ public class DefaultRenderersFactory implements RenderersFactory { return this; } + /** + * Sets whether a secondary {@link MediaCodecVideoRenderer} will be created through {@link + * #buildSecondaryVideoRenderer} for use by {@link ExoPlayer} for pre-warming. + * + *

If enabled, {@link ExoPlayer} will pre-process the video of consecutive media items during + * playback to reduce media item transition latency. + * + *

If {@link #buildVideoRenderers} is overridden to provide custom video renderers then to + * still enable pre-warming,{@link #buildSecondaryVideoRenderer} must also be overridden to + * provide matching secondary custom video renderers. + * + *

This method is experimental. Its default value may change, or it may be renamed or removed + * in a future release. + * + * @param enableMediaCodecVideoRendererPrewarming Whether to enable {@link + * #buildSecondaryVideoRenderer} to provide a secondary {@link MediaCodecVideoRenderer} for + * pre-warming. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + public final DefaultRenderersFactory experimentalSetEnableMediaCodecVideoRendererPrewarming( + boolean enableMediaCodecVideoRendererPrewarming) { + this.enableMediaCodecVideoRendererPrewarming = enableMediaCodecVideoRendererPrewarming; + return this; + } + /** * Sets the maximum duration for which video renderers can attempt to seamlessly join an ongoing * playback. @@ -684,6 +712,76 @@ public class DefaultRenderersFactory implements RenderersFactory { .build(); } + @Override + @Nullable + public Renderer createSecondaryRenderer( + Renderer renderer, + Handler eventHandler, + VideoRendererEventListener videoRendererEventListener, + AudioRendererEventListener audioRendererEventListener, + TextOutput textRendererOutput, + MetadataOutput metadataRendererOutput) { + if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { + return buildSecondaryVideoRenderer( + renderer, + context, + extensionRendererMode, + mediaCodecSelector, + enableDecoderFallback, + eventHandler, + videoRendererEventListener, + allowedVideoJoiningTimeMs); + } + return null; + } + + /** + * Builds a secondary video renderer for an {@link ExoPlayer} instance to use for pre-warming. + * + *

The created secondary video {@code Renderer} should match its primary {@link Renderer} in + * reported track type support and {@link RendererCapabilities}. + * + *

If {@link #buildVideoRenderers} is overridden to provide custom video renderers, then this + * method must also be overridden to supply corresponding custom secondary video renderers. + * + * @param renderer The primary {@link Renderer} for which to create the secondary. + * @param context The {@link Context} associated with the player. + * @param extensionRendererMode The extension renderer mode. + * @param mediaCodecSelector A decoder selector. + * @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder + * initialization fails. This may result in using a decoder that is slower/less efficient than + * the primary decoder. + * @param eventHandler A handler associated with the main thread's looper. + * @param eventListener An event listener. + * @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to + * seamlessly join an ongoing playback, in milliseconds. + * @return The created secondary {@link Renderer renderer instance}. + */ + @Nullable + protected Renderer buildSecondaryVideoRenderer( + Renderer renderer, + Context context, + @ExtensionRendererMode int extensionRendererMode, + MediaCodecSelector mediaCodecSelector, + boolean enableDecoderFallback, + Handler eventHandler, + VideoRendererEventListener eventListener, + long allowedVideoJoiningTimeMs) { + if (enableMediaCodecVideoRendererPrewarming + && renderer.getClass() == MediaCodecVideoRenderer.class) { + return new MediaCodecVideoRenderer( + context, + getCodecAdapterFactory(), + mediaCodecSelector, + allowedVideoJoiningTimeMs, + enableDecoderFallback, + eventHandler, + eventListener, + MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY); + } + return null; + } + /** * Returns the {@link MediaCodecAdapter.Factory} that will be used when creating {@link * androidx.media3.exoplayer.mediacodec.MediaCodecRenderer} instances.