DefaultAssetLoaderFactory.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 androidx.media3.common.util.Assertions.checkNotNull;

import android.content.Context;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.source.MediaSource;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;

/** The default {@link AssetLoader.Factory} implementation. */
@UnstableApi
public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {

  private final Context context;
  private final Codec.DecoderFactory decoderFactory;
  private final boolean forceInterpretHdrAsSdr;
  private final Clock clock;
  private final MediaSource.@MonotonicNonNull Factory mediaSourceFactory;

  private AssetLoader.@MonotonicNonNull Factory imageAssetLoaderFactory;
  private AssetLoader.@MonotonicNonNull Factory exoPlayerAssetLoaderFactory;
  /**
   * Creates an instance.
   *
   * @param context The {@link Context}.
   * @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
   *     necessary).
   * @param forceInterpretHdrAsSdr Whether to apply {@link
   *     TransformationRequest#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR}.
   * @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
   *     testing.
   */
  public DefaultAssetLoaderFactory(
      Context context,
      Codec.DecoderFactory decoderFactory,
      boolean forceInterpretHdrAsSdr,
      Clock clock) {
    this.context = context.getApplicationContext();
    this.decoderFactory = decoderFactory;
    this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
    this.clock = clock;
    this.mediaSourceFactory = null;
  }

  /**
   * Creates an instance.
   *
   * @param context The {@link Context}.
   * @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
   *     necessary).
   * @param forceInterpretHdrAsSdr Whether to apply {@link
   *     TransformationRequest#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR}.
   * @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
   *     testing.
   * @param mediaSourceFactory The {@link MediaSource.Factory} to use to retrieve the samples to
   *     transform when an {@link ExoPlayerAssetLoader} is used.
   */
  public DefaultAssetLoaderFactory(
      Context context,
      Codec.DecoderFactory decoderFactory,
      boolean forceInterpretHdrAsSdr,
      Clock clock,
      MediaSource.Factory mediaSourceFactory) {
    this.context = context.getApplicationContext();
    this.decoderFactory = decoderFactory;
    this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
    this.clock = clock;
    this.mediaSourceFactory = mediaSourceFactory;
  }

  @Override
  public AssetLoader createAssetLoader(
      EditedMediaItem editedMediaItem, Looper looper, AssetLoader.Listener listener) {
    MediaItem mediaItem = editedMediaItem.mediaItem;
    if (isImage(mediaItem.localConfiguration)) {
      if (imageAssetLoaderFactory == null) {
        imageAssetLoaderFactory = new ImageAssetLoader.Factory(context);
      }
      return imageAssetLoaderFactory.createAssetLoader(editedMediaItem, looper, listener);
    }
    if (exoPlayerAssetLoaderFactory == null) {
      exoPlayerAssetLoaderFactory =
          mediaSourceFactory != null
              ? new ExoPlayerAssetLoader.Factory(
                  context, decoderFactory, forceInterpretHdrAsSdr, clock, mediaSourceFactory)
              : new ExoPlayerAssetLoader.Factory(
                  context, decoderFactory, forceInterpretHdrAsSdr, clock);
    }
    return exoPlayerAssetLoaderFactory.createAssetLoader(editedMediaItem, looper, listener);
  }

  private static boolean isImage(@Nullable MediaItem.LocalConfiguration localConfiguration) {
    if (localConfiguration == null) {
      return false;
    }
    ImmutableList<String> supportedImageTypes = ImmutableList.of(".png", ".webp", ".jpg", ".jpeg");
    String uriPath = checkNotNull(localConfiguration.uri.getPath());
    int fileExtensionStart = uriPath.lastIndexOf(".");
    if (fileExtensionStart < 0) {
      return false;
    }
    String extension = uriPath.substring(fileExtensionStart);
    return supportedImageTypes.contains(extension);
  }
}