public class

DefaultRenderersFactory

extends java.lang.Object

implements RenderersFactory

 java.lang.Object

↳androidx.media3.exoplayer.DefaultRenderersFactory

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.0.0-alpha03'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.0.0-alpha03

Artifact androidx.media3:media3-exoplayer:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

Default RenderersFactory implementation.

Summary

Fields
public static final longDEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS

The default maximum duration for which a video renderer can attempt to seamlessly join an ongoing playback.

public static final intEXTENSION_RENDERER_MODE_OFF

Do not allow use of extension renderers.

public static final intEXTENSION_RENDERER_MODE_ON

Allow use of extension renderers.

public static final intEXTENSION_RENDERER_MODE_PREFER

Allow use of extension renderers.

public static final intMAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY

The maximum number of frames that can be dropped between invocations of VideoRendererEventListener.onDroppedFrames(int, long).

Constructors
publicDefaultRenderersFactory(Context context)

Methods
protected voidbuildAudioRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, AudioSink audioSink, Handler eventHandler, AudioRendererEventListener eventListener, java.util.ArrayList<Renderer> out)

Builds audio renderers for use by the player.

protected AudioSinkbuildAudioSink(Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams, boolean enableOffload)

Builds an AudioSink to which the audio renderers will output.

protected voidbuildCameraMotionRenderers(Context context, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds camera motion renderers for use by the player.

protected voidbuildMetadataRenderers(Context context, MetadataOutput output, Looper outputLooper, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds metadata renderers for use by the player.

protected voidbuildMiscellaneousRenderers(Context context, Handler eventHandler, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds any miscellaneous renderers used by the player.

protected voidbuildTextRenderers(Context context, TextOutput output, Looper outputLooper, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds text renderers for use by the player.

protected voidbuildVideoRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, Handler eventHandler, VideoRendererEventListener eventListener, long allowedVideoJoiningTimeMs, java.util.ArrayList<Renderer> out)

Builds video renderers for use by the player.

public RenderercreateRenderers(Handler eventHandler, VideoRendererEventListener videoRendererEventListener, AudioRendererEventListener audioRendererEventListener, TextOutput textRendererOutput, MetadataOutput metadataRendererOutput)

public DefaultRenderersFactoryexperimentalSetImmediateCodecStartAfterFlushEnabled(boolean enabled)

Enable calling MediaCodec immediately after MediaCodec on the playback thread, when operating the codec in asynchronous mode.

public DefaultRenderersFactoryexperimentalSetSynchronizeCodecInteractionsWithQueueingEnabled(boolean enabled)

Enable synchronizing codec interactions with asynchronous buffer queueing.

public DefaultRenderersFactoryforceDisableMediaCodecAsynchronousQueueing()

Disables MediaCodecRenderer instances from operating their MediaCodec in asynchronous mode and perform asynchronous queueing.

public DefaultRenderersFactoryforceEnableMediaCodecAsynchronousQueueing()

Enables MediaCodecRenderer instances to operate their MediaCodec in asynchronous mode and perform asynchronous queueing.

protected MediaCodecAdapter.FactorygetCodecAdapterFactory()

Returns the that will be used when creating MediaCodecRenderer instances.

public DefaultRenderersFactorysetAllowedVideoJoiningTimeMs(long allowedVideoJoiningTimeMs)

Sets the maximum duration for which video renderers can attempt to seamlessly join an ongoing playback.

public DefaultRenderersFactorysetEnableAudioFloatOutput(boolean enableFloatOutput)

Sets whether floating point audio should be output when possible.

public DefaultRenderersFactorysetEnableAudioOffload(boolean enableOffload)

Sets whether audio should be played using the offload path.

public DefaultRenderersFactorysetEnableAudioTrackPlaybackParams(boolean enableAudioTrackPlaybackParams)

Sets whether to enable setting playback speed using , which is supported from API level 23, rather than using application-level audio speed adjustment.

public DefaultRenderersFactorysetEnableDecoderFallback(boolean enableDecoderFallback)

Sets whether to enable fallback to lower-priority decoders if decoder initialization fails.

public DefaultRenderersFactorysetExtensionRendererMode(int extensionRendererMode)

Sets the extension renderer mode, which determines if and how available extension renderers are used.

public DefaultRenderersFactorysetMediaCodecSelector(MediaCodecSelector mediaCodecSelector)

Sets a MediaCodecSelector for use by MediaCodec based renderers.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final long DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS

The default maximum duration for which a video renderer can attempt to seamlessly join an ongoing playback.

public static final int EXTENSION_RENDERER_MODE_OFF

Do not allow use of extension renderers.

public static final int EXTENSION_RENDERER_MODE_ON

Allow use of extension renderers. Extension renderers are indexed after core renderers of the same type. A TrackSelector that prefers the first suitable renderer will therefore prefer to use a core renderer to an extension renderer in the case that both are able to play a given track.

public static final int EXTENSION_RENDERER_MODE_PREFER

Allow use of extension renderers. Extension renderers are indexed before core renderers of the same type. A TrackSelector that prefers the first suitable renderer will therefore prefer to use an extension renderer to a core renderer in the case that both are able to play a given track.

public static final int MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY

The maximum number of frames that can be dropped between invocations of VideoRendererEventListener.onDroppedFrames(int, long).

Constructors

public DefaultRenderersFactory(Context context)

Parameters:

context: A .

Methods

public DefaultRenderersFactory setExtensionRendererMode(int extensionRendererMode)

Sets the extension renderer mode, which determines if and how available extension renderers are used. Note that extensions must be included in the application build for them to be considered available.

The default value is DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF.

Parameters:

extensionRendererMode: The extension renderer mode.

Returns:

This factory, for convenience.

public DefaultRenderersFactory forceEnableMediaCodecAsynchronousQueueing()

Enables MediaCodecRenderer instances to operate their MediaCodec in asynchronous mode and perform asynchronous queueing.

This feature can be enabled only on devices with API versions >= 23. For devices with older API versions, this method is a no-op.

Returns:

This factory, for convenience.

public DefaultRenderersFactory forceDisableMediaCodecAsynchronousQueueing()

Disables MediaCodecRenderer instances from operating their MediaCodec in asynchronous mode and perform asynchronous queueing. MediaCodec instances will be operated synchronous mode.

Returns:

This factory, for convenience.

public DefaultRenderersFactory experimentalSetSynchronizeCodecInteractionsWithQueueingEnabled(boolean enabled)

Enable synchronizing codec interactions with asynchronous buffer queueing.

This method is experimental, and will be renamed or removed in a future release.

Parameters:

enabled: Whether codec interactions will be synchronized with asynchronous buffer queueing.

Returns:

This factory, for convenience.

public DefaultRenderersFactory experimentalSetImmediateCodecStartAfterFlushEnabled(boolean enabled)

Enable calling MediaCodec immediately after MediaCodec on the playback thread, when operating the codec in asynchronous mode. If disabled, MediaCodec will be called by the callback thread after pending callbacks are handled.

By default, this feature is disabled.

This method is experimental, and will be renamed or removed in a future release.

Parameters:

enabled: Whether MediaCodec will be called on the playback thread immediately after MediaCodec.

Returns:

This factory, for convenience.

public DefaultRenderersFactory setEnableDecoderFallback(boolean enableDecoderFallback)

Sets whether to enable fallback to lower-priority decoders if decoder initialization fails. This may result in using a decoder that is less efficient or slower than the primary decoder.

Parameters:

enableDecoderFallback: Whether to enable fallback to lower-priority decoders if decoder initialization fails.

Returns:

This factory, for convenience.

public DefaultRenderersFactory setMediaCodecSelector(MediaCodecSelector mediaCodecSelector)

Sets a MediaCodecSelector for use by MediaCodec based renderers.

The default value is MediaCodecSelector.DEFAULT.

Parameters:

mediaCodecSelector: The MediaCodecSelector.

Returns:

This factory, for convenience.

public DefaultRenderersFactory setEnableAudioFloatOutput(boolean enableFloatOutput)

Sets whether floating point audio should be output when possible.

Enabling floating point output disables audio processing, but may allow for higher quality audio output.

The default value is false.

Parameters:

enableFloatOutput: Whether to enable use of floating point audio output, if available.

Returns:

This factory, for convenience.

public DefaultRenderersFactory setEnableAudioOffload(boolean enableOffload)

Sets whether audio should be played using the offload path.

Audio offload disables ExoPlayer audio processing, but significantly reduces the energy consumption of the playback when offload scheduling is enabled.

Most Android devices can only support one offload at a time and can invalidate it at any time. Thus an app can never be guaranteed that it will be able to play in offload.

The default value is false.

Parameters:

enableOffload: Whether to enable use of audio offload for supported formats, if available.

Returns:

This factory, for convenience.

public DefaultRenderersFactory setEnableAudioTrackPlaybackParams(boolean enableAudioTrackPlaybackParams)

Sets whether to enable setting playback speed using , which is supported from API level 23, rather than using application-level audio speed adjustment. This setting has no effect on builds before API level 23 (application-level speed adjustment will be used in all cases).

If enabled and supported, new playback speed settings will take effect more quickly because they are applied at the audio mixer, rather than at the point of writing data to the track.

When using this mode, the maximum supported playback speed is limited by the size of the audio track's buffer. If the requested speed is not supported the player's event listener will be notified twice on setting playback speed, once with the requested speed, then again with the old playback speed reflecting the fact that the requested speed was not supported.

Parameters:

enableAudioTrackPlaybackParams: Whether to enable setting playback speed using .

Returns:

This factory, for convenience.

public DefaultRenderersFactory setAllowedVideoJoiningTimeMs(long allowedVideoJoiningTimeMs)

Sets the maximum duration for which video renderers can attempt to seamlessly join an ongoing playback.

The default value is DefaultRenderersFactory.DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS.

Parameters:

allowedVideoJoiningTimeMs: The maximum duration for which video renderers can attempt to seamlessly join an ongoing playback, in milliseconds.

Returns:

This factory, for convenience.

public Renderer createRenderers(Handler eventHandler, VideoRendererEventListener videoRendererEventListener, AudioRendererEventListener audioRendererEventListener, TextOutput textRendererOutput, MetadataOutput metadataRendererOutput)

protected void buildVideoRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, Handler eventHandler, VideoRendererEventListener eventListener, long allowedVideoJoiningTimeMs, java.util.ArrayList<Renderer> out)

Builds video renderers for use by the player.

Parameters:

context: The associated with the player.
extensionRendererMode: The extension renderer mode.
mediaCodecSelector: A decoder selector.
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.
eventHandler: A handler associated with the main thread's looper.
eventListener: An event listener.
allowedVideoJoiningTimeMs: The maximum duration for which video renderers can attempt to seamlessly join an ongoing playback, in milliseconds.
out: An array to which the built renderers should be appended.

protected void buildAudioRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, AudioSink audioSink, Handler eventHandler, AudioRendererEventListener eventListener, java.util.ArrayList<Renderer> out)

Builds audio renderers for use by the player.

Parameters:

context: The associated with the player.
extensionRendererMode: The extension renderer mode.
mediaCodecSelector: A decoder selector.
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.
audioSink: A sink to which the renderers will output.
eventHandler: A handler to use when invoking event listeners and outputs.
eventListener: An event listener.
out: An array to which the built renderers should be appended.

protected void buildTextRenderers(Context context, TextOutput output, Looper outputLooper, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds text renderers for use by the player.

Parameters:

context: The associated with the player.
output: An output for the renderers.
outputLooper: The looper associated with the thread on which the output should be called.
extensionRendererMode: The extension renderer mode.
out: An array to which the built renderers should be appended.

protected void buildMetadataRenderers(Context context, MetadataOutput output, Looper outputLooper, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds metadata renderers for use by the player.

Parameters:

context: The associated with the player.
output: An output for the renderers.
outputLooper: The looper associated with the thread on which the output should be called.
extensionRendererMode: The extension renderer mode.
out: An array to which the built renderers should be appended.

protected void buildCameraMotionRenderers(Context context, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds camera motion renderers for use by the player.

Parameters:

context: The associated with the player.
extensionRendererMode: The extension renderer mode.
out: An array to which the built renderers should be appended.

protected void buildMiscellaneousRenderers(Context context, Handler eventHandler, int extensionRendererMode, java.util.ArrayList<Renderer> out)

Builds any miscellaneous renderers used by the player.

Parameters:

context: The associated with the player.
eventHandler: A handler to use when invoking event listeners and outputs.
extensionRendererMode: The extension renderer mode.
out: An array to which the built renderers should be appended.

protected AudioSink buildAudioSink(Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams, boolean enableOffload)

Builds an AudioSink to which the audio renderers will output.

Parameters:

context: The associated with the player.
enableFloatOutput: Whether to enable use of floating point audio output, if available.
enableAudioTrackPlaybackParams: Whether to enable setting playback speed using , if supported.
enableOffload: Whether to enable use of audio offload for supported formats, if available.

Returns:

The AudioSink to which the audio renderers will output. May be null if no audio renderers are required. If null is returned then DefaultRenderersFactory.buildAudioRenderers(Context, int, MediaCodecSelector, boolean, AudioSink, Handler, AudioRendererEventListener, ArrayList) will not be called.

protected MediaCodecAdapter.Factory getCodecAdapterFactory()

Returns the that will be used when creating MediaCodecRenderer instances.

Source

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.exoplayer;

import static java.lang.annotation.ElementType.TYPE_USE;

import android.content.Context;
import android.media.MediaCodec;
import android.media.PlaybackParams;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.audio.AudioCapabilities;
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.DefaultAudioSink;
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
import androidx.media3.exoplayer.mediacodec.DefaultMediaCodecAdapterFactory;
import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter;
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector;
import androidx.media3.exoplayer.metadata.MetadataOutput;
import androidx.media3.exoplayer.metadata.MetadataRenderer;
import androidx.media3.exoplayer.text.TextOutput;
import androidx.media3.exoplayer.text.TextRenderer;
import androidx.media3.exoplayer.trackselection.TrackSelector;
import androidx.media3.exoplayer.video.MediaCodecVideoRenderer;
import androidx.media3.exoplayer.video.VideoRendererEventListener;
import androidx.media3.exoplayer.video.spherical.CameraMotionRenderer;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.ArrayList;

/** Default {@link RenderersFactory} implementation. */
@UnstableApi
public class DefaultRenderersFactory implements RenderersFactory {

  /**
   * The default maximum duration for which a video renderer can attempt to seamlessly join an
   * ongoing playback.
   */
  public static final long DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS = 5000;

  /**
   * Modes for using extension renderers. One of {@link #EXTENSION_RENDERER_MODE_OFF}, {@link
   * #EXTENSION_RENDERER_MODE_ON} or {@link #EXTENSION_RENDERER_MODE_PREFER}.
   */
  @Documented
  @Retention(RetentionPolicy.SOURCE)
  @Target(TYPE_USE)
  @IntDef({EXTENSION_RENDERER_MODE_OFF, EXTENSION_RENDERER_MODE_ON, EXTENSION_RENDERER_MODE_PREFER})
  public @interface ExtensionRendererMode {}
  /** Do not allow use of extension renderers. */
  public static final int EXTENSION_RENDERER_MODE_OFF = 0;
  /**
   * Allow use of extension renderers. Extension renderers are indexed after core renderers of the
   * same type. A {@link TrackSelector} that prefers the first suitable renderer will therefore
   * prefer to use a core renderer to an extension renderer in the case that both are able to play a
   * given track.
   */
  public static final int EXTENSION_RENDERER_MODE_ON = 1;
  /**
   * Allow use of extension renderers. Extension renderers are indexed before core renderers of the
   * same type. A {@link TrackSelector} that prefers the first suitable renderer will therefore
   * prefer to use an extension renderer to a core renderer in the case that both are able to play a
   * given track.
   */
  public static final int EXTENSION_RENDERER_MODE_PREFER = 2;

  /**
   * The maximum number of frames that can be dropped between invocations of {@link
   * VideoRendererEventListener#onDroppedFrames(int, long)}.
   */
  public static final int MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY = 50;

  private static final String TAG = "DefaultRenderersFactory";

  private final Context context;
  private final DefaultMediaCodecAdapterFactory codecAdapterFactory;
  private @ExtensionRendererMode int extensionRendererMode;
  private long allowedVideoJoiningTimeMs;
  private boolean enableDecoderFallback;
  private MediaCodecSelector mediaCodecSelector;
  private boolean enableFloatOutput;
  private boolean enableAudioTrackPlaybackParams;
  private boolean enableOffload;

  /** @param context A {@link Context}. */
  public DefaultRenderersFactory(Context context) {
    this.context = context;
    codecAdapterFactory = new DefaultMediaCodecAdapterFactory();
    extensionRendererMode = EXTENSION_RENDERER_MODE_OFF;
    allowedVideoJoiningTimeMs = DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS;
    mediaCodecSelector = MediaCodecSelector.DEFAULT;
  }

  /**
   * Sets the extension renderer mode, which determines if and how available extension renderers are
   * used. Note that extensions must be included in the application build for them to be considered
   * available.
   *
   * <p>The default value is {@link #EXTENSION_RENDERER_MODE_OFF}.
   *
   * @param extensionRendererMode The extension renderer mode.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory setExtensionRendererMode(
      @ExtensionRendererMode int extensionRendererMode) {
    this.extensionRendererMode = extensionRendererMode;
    return this;
  }

  /**
   * Enables {@link androidx.media3.exoplayer.mediacodec.MediaCodecRenderer} instances to operate
   * their {@link MediaCodec} in asynchronous mode and perform asynchronous queueing.
   *
   * <p>This feature can be enabled only on devices with API versions &gt;= 23. For devices with
   * older API versions, this method is a no-op.
   *
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory forceEnableMediaCodecAsynchronousQueueing() {
    codecAdapterFactory.forceEnableAsynchronous();
    return this;
  }

  /**
   * Disables {@link androidx.media3.exoplayer.mediacodec.MediaCodecRenderer} instances from
   * operating their {@link MediaCodec} in asynchronous mode and perform asynchronous queueing.
   * {@link MediaCodec} instances will be operated synchronous mode.
   *
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory forceDisableMediaCodecAsynchronousQueueing() {
    codecAdapterFactory.forceDisableAsynchronous();
    return this;
  }

  /**
   * Enable synchronizing codec interactions with asynchronous buffer queueing.
   *
   * <p>This method is experimental, and will be renamed or removed in a future release.
   *
   * @param enabled Whether codec interactions will be synchronized with asynchronous buffer
   *     queueing.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory experimentalSetSynchronizeCodecInteractionsWithQueueingEnabled(
      boolean enabled) {
    codecAdapterFactory.experimentalSetSynchronizeCodecInteractionsWithQueueingEnabled(enabled);
    return this;
  }

  /**
   * Enable calling {@link MediaCodec#start} immediately after {@link MediaCodec#flush} on the
   * playback thread, when operating the codec in asynchronous mode. If disabled, {@link
   * MediaCodec#start} will be called by the callback thread after pending callbacks are handled.
   *
   * <p>By default, this feature is disabled.
   *
   * <p>This method is experimental, and will be renamed or removed in a future release.
   *
   * @param enabled Whether {@link MediaCodec#start} will be called on the playback thread
   *     immediately after {@link MediaCodec#flush}.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory experimentalSetImmediateCodecStartAfterFlushEnabled(
      boolean enabled) {
    codecAdapterFactory.experimentalSetImmediateCodecStartAfterFlushEnabled(enabled);
    return this;
  }

  /**
   * Sets whether to enable fallback to lower-priority decoders if decoder initialization fails.
   * This may result in using a decoder that is less efficient or slower than the primary decoder.
   *
   * @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
   *     initialization fails.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory setEnableDecoderFallback(boolean enableDecoderFallback) {
    this.enableDecoderFallback = enableDecoderFallback;
    return this;
  }

  /**
   * Sets a {@link MediaCodecSelector} for use by {@link MediaCodec} based renderers.
   *
   * <p>The default value is {@link MediaCodecSelector#DEFAULT}.
   *
   * @param mediaCodecSelector The {@link MediaCodecSelector}.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory setMediaCodecSelector(MediaCodecSelector mediaCodecSelector) {
    this.mediaCodecSelector = mediaCodecSelector;
    return this;
  }

  /**
   * Sets whether floating point audio should be output when possible.
   *
   * <p>Enabling floating point output disables audio processing, but may allow for higher quality
   * audio output.
   *
   * <p>The default value is {@code false}.
   *
   * @param enableFloatOutput Whether to enable use of floating point audio output, if available.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory setEnableAudioFloatOutput(boolean enableFloatOutput) {
    this.enableFloatOutput = enableFloatOutput;
    return this;
  }

  /**
   * Sets whether audio should be played using the offload path.
   *
   * <p>Audio offload disables ExoPlayer audio processing, but significantly reduces the energy
   * consumption of the playback when {@link
   * ExoPlayer#experimentalSetOffloadSchedulingEnabled(boolean) offload scheduling} is enabled.
   *
   * <p>Most Android devices can only support one offload {@link android.media.AudioTrack} at a time
   * and can invalidate it at any time. Thus an app can never be guaranteed that it will be able to
   * play in offload.
   *
   * <p>The default value is {@code false}.
   *
   * @param enableOffload Whether to enable use of audio offload for supported formats, if
   *     available.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory setEnableAudioOffload(boolean enableOffload) {
    this.enableOffload = enableOffload;
    return this;
  }

  /**
   * Sets whether to enable setting playback speed using {@link
   * android.media.AudioTrack#setPlaybackParams(PlaybackParams)}, which is supported from API level
   * 23, rather than using application-level audio speed adjustment. This setting has no effect on
   * builds before API level 23 (application-level speed adjustment will be used in all cases).
   *
   * <p>If enabled and supported, new playback speed settings will take effect more quickly because
   * they are applied at the audio mixer, rather than at the point of writing data to the track.
   *
   * <p>When using this mode, the maximum supported playback speed is limited by the size of the
   * audio track's buffer. If the requested speed is not supported the player's event listener will
   * be notified twice on setting playback speed, once with the requested speed, then again with the
   * old playback speed reflecting the fact that the requested speed was not supported.
   *
   * @param enableAudioTrackPlaybackParams Whether to enable setting playback speed using {@link
   *     android.media.AudioTrack#setPlaybackParams(PlaybackParams)}.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory setEnableAudioTrackPlaybackParams(
      boolean enableAudioTrackPlaybackParams) {
    this.enableAudioTrackPlaybackParams = enableAudioTrackPlaybackParams;
    return this;
  }

  /**
   * Sets the maximum duration for which video renderers can attempt to seamlessly join an ongoing
   * playback.
   *
   * <p>The default value is {@link #DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS}.
   *
   * @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to
   *     seamlessly join an ongoing playback, in milliseconds.
   * @return This factory, for convenience.
   */
  public DefaultRenderersFactory setAllowedVideoJoiningTimeMs(long allowedVideoJoiningTimeMs) {
    this.allowedVideoJoiningTimeMs = allowedVideoJoiningTimeMs;
    return this;
  }

  @Override
  public Renderer[] createRenderers(
      Handler eventHandler,
      VideoRendererEventListener videoRendererEventListener,
      AudioRendererEventListener audioRendererEventListener,
      TextOutput textRendererOutput,
      MetadataOutput metadataRendererOutput) {
    ArrayList<Renderer> renderersList = new ArrayList<>();
    buildVideoRenderers(
        context,
        extensionRendererMode,
        mediaCodecSelector,
        enableDecoderFallback,
        eventHandler,
        videoRendererEventListener,
        allowedVideoJoiningTimeMs,
        renderersList);
    @Nullable
    AudioSink audioSink =
        buildAudioSink(context, enableFloatOutput, enableAudioTrackPlaybackParams, enableOffload);
    if (audioSink != null) {
      buildAudioRenderers(
          context,
          extensionRendererMode,
          mediaCodecSelector,
          enableDecoderFallback,
          audioSink,
          eventHandler,
          audioRendererEventListener,
          renderersList);
    }
    buildTextRenderers(
        context,
        textRendererOutput,
        eventHandler.getLooper(),
        extensionRendererMode,
        renderersList);
    buildMetadataRenderers(
        context,
        metadataRendererOutput,
        eventHandler.getLooper(),
        extensionRendererMode,
        renderersList);
    buildCameraMotionRenderers(context, extensionRendererMode, renderersList);
    buildMiscellaneousRenderers(context, eventHandler, extensionRendererMode, renderersList);
    return renderersList.toArray(new Renderer[0]);
  }

  /**
   * Builds video renderers for use by the player.
   *
   * @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.
   * @param out An array to which the built renderers should be appended.
   */
  protected void buildVideoRenderers(
      Context context,
      @ExtensionRendererMode int extensionRendererMode,
      MediaCodecSelector mediaCodecSelector,
      boolean enableDecoderFallback,
      Handler eventHandler,
      VideoRendererEventListener eventListener,
      long allowedVideoJoiningTimeMs,
      ArrayList<Renderer> out) {
    MediaCodecVideoRenderer videoRenderer =
        new MediaCodecVideoRenderer(
            context,
            getCodecAdapterFactory(),
            mediaCodecSelector,
            allowedVideoJoiningTimeMs,
            enableDecoderFallback,
            eventHandler,
            eventListener,
            MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
    out.add(videoRenderer);

    if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
      return;
    }
    int extensionRendererIndex = out.size();
    if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
      extensionRendererIndex--;
    }

    try {
      // Full class names used for constructor args so the LINT rule triggers if any of them move.
      Class<?> clazz = Class.forName("androidx.media3.decoder.vp9.LibvpxVideoRenderer");
      Constructor<?> constructor =
          clazz.getConstructor(
              long.class,
              android.os.Handler.class,
              androidx.media3.exoplayer.video.VideoRendererEventListener.class,
              int.class);
      Renderer renderer =
          (Renderer)
              constructor.newInstance(
                  allowedVideoJoiningTimeMs,
                  eventHandler,
                  eventListener,
                  MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
      out.add(extensionRendererIndex++, renderer);
      Log.i(TAG, "Loaded LibvpxVideoRenderer.");
    } catch (ClassNotFoundException e) {
      // Expected if the app was built without the extension.
    } catch (Exception e) {
      // The extension is present, but instantiation failed.
      throw new RuntimeException("Error instantiating VP9 extension", e);
    }

    try {
      // Full class names used for constructor args so the LINT rule triggers if any of them move.
      Class<?> clazz = Class.forName("androidx.media3.decoder.av1.Libgav1VideoRenderer");
      Constructor<?> constructor =
          clazz.getConstructor(
              long.class,
              android.os.Handler.class,
              androidx.media3.exoplayer.video.VideoRendererEventListener.class,
              int.class);
      Renderer renderer =
          (Renderer)
              constructor.newInstance(
                  allowedVideoJoiningTimeMs,
                  eventHandler,
                  eventListener,
                  MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
      out.add(extensionRendererIndex++, renderer);
      Log.i(TAG, "Loaded Libgav1VideoRenderer.");
    } catch (ClassNotFoundException e) {
      // Expected if the app was built without the extension.
    } catch (Exception e) {
      // The extension is present, but instantiation failed.
      throw new RuntimeException("Error instantiating AV1 extension", e);
    }
  }

  /**
   * Builds audio renderers for use by the player.
   *
   * @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 audioSink A sink to which the renderers will output.
   * @param eventHandler A handler to use when invoking event listeners and outputs.
   * @param eventListener An event listener.
   * @param out An array to which the built renderers should be appended.
   */
  protected void buildAudioRenderers(
      Context context,
      @ExtensionRendererMode int extensionRendererMode,
      MediaCodecSelector mediaCodecSelector,
      boolean enableDecoderFallback,
      AudioSink audioSink,
      Handler eventHandler,
      AudioRendererEventListener eventListener,
      ArrayList<Renderer> out) {
    MediaCodecAudioRenderer audioRenderer =
        new MediaCodecAudioRenderer(
            context,
            getCodecAdapterFactory(),
            mediaCodecSelector,
            enableDecoderFallback,
            eventHandler,
            eventListener,
            audioSink);
    out.add(audioRenderer);

    if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
      return;
    }
    int extensionRendererIndex = out.size();
    if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
      extensionRendererIndex--;
    }

    try {
      // Full class names used for constructor args so the LINT rule triggers if any of them move.
      Class<?> clazz = Class.forName("androidx.media3.decoder.opus.LibopusAudioRenderer");
      Constructor<?> constructor =
          clazz.getConstructor(
              android.os.Handler.class,
              androidx.media3.exoplayer.audio.AudioRendererEventListener.class,
              androidx.media3.exoplayer.audio.AudioSink.class);
      Renderer renderer =
          (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
      out.add(extensionRendererIndex++, renderer);
      Log.i(TAG, "Loaded LibopusAudioRenderer.");
    } catch (ClassNotFoundException e) {
      // Expected if the app was built without the extension.
    } catch (Exception e) {
      // The extension is present, but instantiation failed.
      throw new RuntimeException("Error instantiating Opus extension", e);
    }

    try {
      // Full class names used for constructor args so the LINT rule triggers if any of them move.
      Class<?> clazz = Class.forName("androidx.media3.decoder.flac.LibflacAudioRenderer");
      Constructor<?> constructor =
          clazz.getConstructor(
              android.os.Handler.class,
              androidx.media3.exoplayer.audio.AudioRendererEventListener.class,
              androidx.media3.exoplayer.audio.AudioSink.class);
      Renderer renderer =
          (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
      out.add(extensionRendererIndex++, renderer);
      Log.i(TAG, "Loaded LibflacAudioRenderer.");
    } catch (ClassNotFoundException e) {
      // Expected if the app was built without the extension.
    } catch (Exception e) {
      // The extension is present, but instantiation failed.
      throw new RuntimeException("Error instantiating FLAC extension", e);
    }

    try {
      // Full class names used for constructor args so the LINT rule triggers if any of them move.
      Class<?> clazz = Class.forName("androidx.media3.decoder.ffmpeg.FfmpegAudioRenderer");
      Constructor<?> constructor =
          clazz.getConstructor(
              android.os.Handler.class,
              androidx.media3.exoplayer.audio.AudioRendererEventListener.class,
              androidx.media3.exoplayer.audio.AudioSink.class);
      Renderer renderer =
          (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
      out.add(extensionRendererIndex++, renderer);
      Log.i(TAG, "Loaded FfmpegAudioRenderer.");
    } catch (ClassNotFoundException e) {
      // Expected if the app was built without the extension.
    } catch (Exception e) {
      // The extension is present, but instantiation failed.
      throw new RuntimeException("Error instantiating FFmpeg extension", e);
    }
  }

  /**
   * Builds text renderers for use by the player.
   *
   * @param context The {@link Context} associated with the player.
   * @param output An output for the renderers.
   * @param outputLooper The looper associated with the thread on which the output should be called.
   * @param extensionRendererMode The extension renderer mode.
   * @param out An array to which the built renderers should be appended.
   */
  protected void buildTextRenderers(
      Context context,
      TextOutput output,
      Looper outputLooper,
      @ExtensionRendererMode int extensionRendererMode,
      ArrayList<Renderer> out) {
    out.add(new TextRenderer(output, outputLooper));
  }

  /**
   * Builds metadata renderers for use by the player.
   *
   * @param context The {@link Context} associated with the player.
   * @param output An output for the renderers.
   * @param outputLooper The looper associated with the thread on which the output should be called.
   * @param extensionRendererMode The extension renderer mode.
   * @param out An array to which the built renderers should be appended.
   */
  protected void buildMetadataRenderers(
      Context context,
      MetadataOutput output,
      Looper outputLooper,
      @ExtensionRendererMode int extensionRendererMode,
      ArrayList<Renderer> out) {
    out.add(new MetadataRenderer(output, outputLooper));
  }

  /**
   * Builds camera motion renderers for use by the player.
   *
   * @param context The {@link Context} associated with the player.
   * @param extensionRendererMode The extension renderer mode.
   * @param out An array to which the built renderers should be appended.
   */
  protected void buildCameraMotionRenderers(
      Context context, @ExtensionRendererMode int extensionRendererMode, ArrayList<Renderer> out) {
    out.add(new CameraMotionRenderer());
  }

  /**
   * Builds any miscellaneous renderers used by the player.
   *
   * @param context The {@link Context} associated with the player.
   * @param eventHandler A handler to use when invoking event listeners and outputs.
   * @param extensionRendererMode The extension renderer mode.
   * @param out An array to which the built renderers should be appended.
   */
  protected void buildMiscellaneousRenderers(
      Context context,
      Handler eventHandler,
      @ExtensionRendererMode int extensionRendererMode,
      ArrayList<Renderer> out) {
    // Do nothing.
  }

  /**
   * Builds an {@link AudioSink} to which the audio renderers will output.
   *
   * @param context The {@link Context} associated with the player.
   * @param enableFloatOutput Whether to enable use of floating point audio output, if available.
   * @param enableAudioTrackPlaybackParams Whether to enable setting playback speed using {@link
   *     android.media.AudioTrack#setPlaybackParams(PlaybackParams)}, if supported.
   * @param enableOffload Whether to enable use of audio offload for supported formats, if
   *     available.
   * @return The {@link AudioSink} to which the audio renderers will output. May be {@code null} if
   *     no audio renderers are required. If {@code null} is returned then {@link
   *     #buildAudioRenderers} will not be called.
   */
  @Nullable
  protected AudioSink buildAudioSink(
      Context context,
      boolean enableFloatOutput,
      boolean enableAudioTrackPlaybackParams,
      boolean enableOffload) {
    return new DefaultAudioSink.Builder()
        .setAudioCapabilities(AudioCapabilities.getCapabilities(context))
        .setEnableFloatOutput(enableFloatOutput)
        .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
        .setOffloadMode(
            enableOffload
                ? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED
                : DefaultAudioSink.OFFLOAD_MODE_DISABLED)
        .build();
  }

  /**
   * Returns the {@link MediaCodecAdapter.Factory} that will be used when creating {@link
   * androidx.media3.exoplayer.mediacodec.MediaCodecRenderer} instances.
   */
  protected MediaCodecAdapter.Factory getCodecAdapterFactory() {
    return codecAdapterFactory;
  }
}