AssetLoader.java

/*
 * Copyright 2022 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.transformer;

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

import android.os.Looper;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.media3.common.Format;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableMap;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Provides media data to a {@linkplain Transformer}.
 *
 * <p>The output samples can be encoded or decoded.
 *
 * <p>Only audio and video samples are supported. Both audio and video tracks can be provided by a
 * single asset loader, but outputting multiple tracks of the same type is not supported.
 *
 * <p>An asset loader is responsible for {@linkplain EditedMediaItem.Builder#setRemoveAudio(boolean)
 * removing audio} or {@linkplain EditedMediaItem.Builder#setRemoveVideo(boolean) video} if
 * requested.
 *
 * <p>If {@linkplain EditedMediaItem.Builder#setFlattenForSlowMotion(boolean) slow motion
 * flattening} is requested, the asset loader should flatten the video track for media containing
 * slow motion markers. This is usually done prior to decoding. The audio samples are flattened
 * after they are output by the {@link AssetLoader}, because this is done on decoded samples.
 */
@UnstableApi
public interface AssetLoader {

  /** A factory for {@link AssetLoader} instances. */
  interface Factory {

    /**
     * Creates an {@link AssetLoader} instance.
     *
     * @param editedMediaItem The {@link EditedMediaItem} to load.
     * @param looper The {@link Looper} that's used to access the {@link AssetLoader} after it's
     *     been created.
     * @param listener The {@link Listener} on which the {@link AssetLoader} should notify of
     *     events.
     * @return An {@link AssetLoader}.
     */
    AssetLoader createAssetLoader(
        EditedMediaItem editedMediaItem, Looper looper, Listener listener);
  }

  /** A listener of {@link AssetLoader} events. */
  interface Listener {

    /**
     * Called when the duration of the input media is known.
     *
     * <p>Can be called from any thread.
     */
    void onDurationUs(long durationUs);

    /**
     * Called when the number of tracks being output is known.
     *
     * <p>Can be called from any thread.
     */
    void onTrackCount(@IntRange(from = 1) int trackCount);

    /**
     * Called when the information on a track is known.
     *
     * <p>Must be called after the {@linkplain #onDurationUs(long) duration} and the {@linkplain
     * #onTrackCount(int) track count} have been reported.
     *
     * <p>Must be called once per {@linkplain #onTrackCount(int) declared} track.
     *
     * <p>Must be called from the thread that will be used to call {@link #onOutputFormat(Format)}.
     *
     * @param inputFormat The {@link Format} of samples that will be input to the {@link
     *     AssetLoader} (prior to video slow motion flattening or to decoding).
     * @param supportedOutputTypes The output {@linkplain SupportedOutputTypes types} supported by
     *     this {@link AssetLoader} for the track added. At least one output type must be supported.
     * @return Whether the {@link AssetLoader} needs to provide decoded data to the {@link
     *     SampleConsumer}.
     */
    boolean onTrackAdded(Format inputFormat, @SupportedOutputTypes int supportedOutputTypes);

    /**
     * Called when the {@link Format} of samples that will be output by the {@link AssetLoader} is
     * known.
     *
     * <p>Must be called after the corresponding track has been {@link #onTrackAdded added}.
     *
     * <p>For each {@link #onTrackAdded added} track, this method must be called regularly until the
     * returned {@link SampleConsumer} is non-null.
     *
     * <p>Must be called from the thread that will be used to call the returned {@link
     * SampleConsumer}'s methods. This thread must be the same for all formats output, and is
     * generally different from the one used to access the {@link AssetLoader} methods.
     *
     * @param format The {@link Format} of samples that will be output.
     * @return The {@link SampleConsumer} of samples of the given {@link Format}, or {@code null} if
     *     it could not be retrieved yet.
     * @throws ExportException If an error occurs configuring the {@link SampleConsumer}.
     */
    @Nullable
    SampleConsumer onOutputFormat(Format format) throws ExportException;

    /**
     * Called if an error occurs in the asset loader. In this case, the asset loader will be
     * {@linkplain #release() released} automatically.
     *
     * <p>Can be called from any thread.
     */
    void onError(ExportException exportException);
  }

  /**
   * Supported output types of an asset loader. Possible flag values are {@link
   * #SUPPORTED_OUTPUT_TYPE_ENCODED} and {@link #SUPPORTED_OUTPUT_TYPE_DECODED}.
   */
  @Documented
  @Retention(RetentionPolicy.SOURCE)
  @Target(TYPE_USE)
  @IntDef(
      flag = true,
      value = {
        SUPPORTED_OUTPUT_TYPE_ENCODED,
        SUPPORTED_OUTPUT_TYPE_DECODED,
      })
  @interface SupportedOutputTypes {}
  /** Indicates that the asset loader can output encoded samples. */
  int SUPPORTED_OUTPUT_TYPE_ENCODED = 1;
  /** Indicates that the asset loader can output decoded samples. */
  int SUPPORTED_OUTPUT_TYPE_DECODED = 1 << 1;

  /** Starts the asset loader. */
  void start();

  /**
   * Returns the current {@link Transformer.ProgressState} and updates {@code progressHolder} with
   * the current progress if it is {@link Transformer#PROGRESS_STATE_AVAILABLE available}.
   *
   * @param progressHolder A {@link ProgressHolder}, updated to hold the percentage progress if
   *     {@link Transformer#PROGRESS_STATE_AVAILABLE available}.
   * @return The {@link Transformer.ProgressState}.
   */
  @Transformer.ProgressState
  int getProgress(ProgressHolder progressHolder);

  /**
   * Return the used decoders' names.
   *
   * @return The decoders' names keyed by {@linkplain androidx.media3.common.C.TrackType track
   *     type}.
   */
  ImmutableMap<Integer, String> getDecoderNames();

  /** Stops loading data and releases all resources associated with the asset loader. */
  void release();
}