mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Pass an EditedMediaItem to AssetLoader.Factory
PiperOrigin-RevId: 505671326
This commit is contained in:
parent
32d4686c56
commit
f5e31989ab
@ -22,10 +22,8 @@ import android.os.Looper;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@ -38,61 +36,34 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
* <p>The setters in this interface will be called with the values used to build and start the
|
||||
* {@link Transformer}.
|
||||
*/
|
||||
/** A factory for {@link AssetLoader} instances. */
|
||||
interface Factory {
|
||||
|
||||
/**
|
||||
* Sets whether to remove the audio samples from the output (if any).
|
||||
* Creates an {@link AssetLoader} instance.
|
||||
*
|
||||
* <p>The audio and video cannot both be removed because the output would not contain any
|
||||
* samples.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
Factory setRemoveAudio(boolean removeAudio);
|
||||
|
||||
/**
|
||||
* Sets whether to remove the video samples from the output (if any).
|
||||
*
|
||||
* <p>The audio and video cannot both be removed because the output would not contain any
|
||||
* samples.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
Factory setRemoveVideo(boolean removeVideo);
|
||||
|
||||
/**
|
||||
* Sets whether the video samples should be flattened prior to decoding for media containing
|
||||
* slow motion markers.
|
||||
*
|
||||
* <p>The audio samples are flattened after they are output by the {@link AssetLoader}, because
|
||||
* this is done on decoded samples.
|
||||
*
|
||||
* <p>For more information on slow motion flattening, see {@link
|
||||
* EditedMediaItem.Builder#setFlattenForSlowMotion(boolean)}.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
Factory setFlattenVideoForSlowMotion(boolean flattenVideoForSlowMotion);
|
||||
|
||||
/**
|
||||
* Creates an {@link AssetLoader} instance. All the setters in this factory must be called
|
||||
* before creating the {@link AssetLoader}.
|
||||
*
|
||||
* @param mediaItem The {@link MediaItem} to load.
|
||||
* @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(MediaItem mediaItem, Looper looper, Listener listener);
|
||||
AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, Listener listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
@ -44,7 +43,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
*/
|
||||
/* package */ final class CompositeAssetLoader implements AssetLoader, AssetLoader.Listener {
|
||||
|
||||
private final List<MediaItem> mediaItems;
|
||||
private final List<EditedMediaItem> editedMediaItems;
|
||||
private final AtomicInteger currentMediaItemIndex;
|
||||
private final AssetLoader.Factory assetLoaderFactory;
|
||||
private final HandlerWrapper handler;
|
||||
@ -58,12 +57,12 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
private volatile long currentDurationUs;
|
||||
|
||||
public CompositeAssetLoader(
|
||||
List<MediaItem> mediaItems,
|
||||
List<EditedMediaItem> editedMediaItems,
|
||||
AssetLoader.Factory assetLoaderFactory,
|
||||
Looper looper,
|
||||
Listener listener,
|
||||
Clock clock) {
|
||||
this.mediaItems = mediaItems;
|
||||
this.editedMediaItems = editedMediaItems;
|
||||
this.assetLoaderFactory = assetLoaderFactory;
|
||||
compositeAssetLoaderListener = listener;
|
||||
currentMediaItemIndex = new AtomicInteger();
|
||||
@ -75,7 +74,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
// constructor.
|
||||
@SuppressWarnings("nullness:argument.type.incompatible")
|
||||
AssetLoader currentAssetLoader =
|
||||
assetLoaderFactory.createAssetLoader(mediaItems.get(0), looper, /* listener= */ this);
|
||||
assetLoaderFactory.createAssetLoader(editedMediaItems.get(0), looper, /* listener= */ this);
|
||||
this.currentAssetLoader = currentAssetLoader;
|
||||
}
|
||||
|
||||
@ -87,7 +86,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
@Override
|
||||
public @Transformer.ProgressState int getProgress(ProgressHolder progressHolder) {
|
||||
int progressState = currentAssetLoader.getProgress(progressHolder);
|
||||
int mediaItemCount = mediaItems.size();
|
||||
int mediaItemCount = editedMediaItems.size();
|
||||
if (mediaItemCount == 1 || progressState == PROGRESS_STATE_NOT_STARTED) {
|
||||
return progressState;
|
||||
}
|
||||
@ -116,7 +115,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
@Override
|
||||
public void onDurationUs(long durationUs) {
|
||||
currentDurationUs = durationUs;
|
||||
if (mediaItems.size() == 1) {
|
||||
if (editedMediaItems.size() == 1) {
|
||||
compositeAssetLoaderListener.onDurationUs(durationUs);
|
||||
} else if (currentMediaItemIndex.get() == 0) {
|
||||
// TODO(b/252537210): support silent audio track for sequence of AssetLoaders (silent audio
|
||||
@ -194,7 +193,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
DecoderInputBuffer inputBuffer = checkStateNotNull(sampleConsumer.getInputBuffer());
|
||||
if (inputBuffer.isEndOfStream()) {
|
||||
nonEndedTracks.decrementAndGet();
|
||||
if (currentMediaItemIndex.get() < mediaItems.size() - 1) {
|
||||
if (currentMediaItemIndex.get() < editedMediaItems.size() - 1) {
|
||||
if (nonEndedTracks.get() == 0) {
|
||||
switchAssetLoader();
|
||||
}
|
||||
@ -233,7 +232,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
@Override
|
||||
public void signalEndOfVideoInput() {
|
||||
nonEndedTracks.decrementAndGet();
|
||||
if (currentMediaItemIndex.get() < mediaItems.size() - 1) {
|
||||
if (currentMediaItemIndex.get() < editedMediaItems.size() - 1) {
|
||||
if (nonEndedTracks.get() == 0) {
|
||||
switchAssetLoader();
|
||||
sampleConsumer.setVideoOffsetToAddUs(totalDurationUs.get());
|
||||
@ -248,10 +247,11 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
handler.post(
|
||||
() -> {
|
||||
currentAssetLoader.release();
|
||||
MediaItem mediaItem = mediaItems.get(currentMediaItemIndex.incrementAndGet());
|
||||
EditedMediaItem editedMediaItem =
|
||||
editedMediaItems.get(currentMediaItemIndex.incrementAndGet());
|
||||
currentAssetLoader =
|
||||
assetLoaderFactory.createAssetLoader(
|
||||
mediaItem,
|
||||
editedMediaItem,
|
||||
checkNotNull(Looper.myLooper()),
|
||||
/* listener= */ CompositeAssetLoader.this);
|
||||
currentAssetLoader.start();
|
||||
|
@ -18,11 +18,9 @@ package androidx.media3.transformer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
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.errorprone.annotations.CanIgnoreReturnValue;
|
||||
|
||||
/** The default {@link AssetLoader.Factory} implementation. */
|
||||
@UnstableApi
|
||||
@ -64,28 +62,9 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
||||
new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock, mediaSourceFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CanIgnoreReturnValue
|
||||
public AssetLoader.Factory setRemoveAudio(boolean removeAudio) {
|
||||
return assetLoaderFactory.setRemoveAudio(removeAudio);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CanIgnoreReturnValue
|
||||
public AssetLoader.Factory setRemoveVideo(boolean removeVideo) {
|
||||
return assetLoaderFactory.setRemoveVideo(removeVideo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CanIgnoreReturnValue
|
||||
public AssetLoader.Factory setFlattenVideoForSlowMotion(boolean flattenVideoForSlowMotion) {
|
||||
assetLoaderFactory.setFlattenVideoForSlowMotion(flattenVideoForSlowMotion);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetLoader createAssetLoader(
|
||||
MediaItem mediaItem, Looper looper, AssetLoader.Listener listener) {
|
||||
return assetLoaderFactory.createAssetLoader(mediaItem, looper, listener);
|
||||
EditedMediaItem editedMediaItem, Looper looper, AssetLoader.Listener listener) {
|
||||
return assetLoaderFactory.createAssetLoader(editedMediaItem, looper, listener);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.PlaybackException;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.Timeline;
|
||||
@ -55,7 +54,6 @@ import androidx.media3.exoplayer.video.VideoRendererEventListener;
|
||||
import androidx.media3.extractor.DefaultExtractorsFactory;
|
||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
|
||||
/** An {@link AssetLoader} implementation that uses an {@link ExoPlayer} to load samples. */
|
||||
@UnstableApi
|
||||
@ -69,10 +67,6 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
private final Clock clock;
|
||||
@Nullable private final MediaSource.Factory mediaSourceFactory;
|
||||
|
||||
private boolean removeAudio;
|
||||
private boolean removeVideo;
|
||||
private boolean flattenVideoForSlowMotion;
|
||||
|
||||
/**
|
||||
* Creates an instance using a {@link DefaultMediaSourceFactory}.
|
||||
*
|
||||
@ -112,51 +106,22 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CanIgnoreReturnValue
|
||||
public AssetLoader.Factory setRemoveAudio(boolean removeAudio) {
|
||||
this.removeAudio = removeAudio;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CanIgnoreReturnValue
|
||||
public AssetLoader.Factory setRemoveVideo(boolean removeVideo) {
|
||||
this.removeVideo = removeVideo;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CanIgnoreReturnValue
|
||||
public AssetLoader.Factory setFlattenVideoForSlowMotion(boolean flattenVideoForSlowMotion) {
|
||||
this.flattenVideoForSlowMotion = flattenVideoForSlowMotion;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetLoader createAssetLoader(MediaItem mediaItem, Looper looper, Listener listener) {
|
||||
public AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, Listener listener) {
|
||||
MediaSource.Factory mediaSourceFactory = this.mediaSourceFactory;
|
||||
if (mediaSourceFactory == null) {
|
||||
DefaultExtractorsFactory defaultExtractorsFactory = new DefaultExtractorsFactory();
|
||||
if (flattenVideoForSlowMotion) {
|
||||
if (editedMediaItem.flattenForSlowMotion) {
|
||||
defaultExtractorsFactory.setMp4ExtractorFlags(Mp4Extractor.FLAG_READ_SEF_DATA);
|
||||
}
|
||||
mediaSourceFactory = new DefaultMediaSourceFactory(context, defaultExtractorsFactory);
|
||||
}
|
||||
return new ExoPlayerAssetLoader(
|
||||
context,
|
||||
mediaItem,
|
||||
removeAudio,
|
||||
removeVideo,
|
||||
flattenVideoForSlowMotion,
|
||||
mediaSourceFactory,
|
||||
decoderFactory,
|
||||
looper,
|
||||
listener,
|
||||
clock);
|
||||
context, editedMediaItem, mediaSourceFactory, decoderFactory, looper, listener, clock);
|
||||
}
|
||||
}
|
||||
|
||||
private final MediaItem mediaItem;
|
||||
private final EditedMediaItem editedMediaItem;
|
||||
private final CapturingDecoderFactory decoderFactory;
|
||||
private final ExoPlayer player;
|
||||
|
||||
@ -164,16 +129,13 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
|
||||
private ExoPlayerAssetLoader(
|
||||
Context context,
|
||||
MediaItem mediaItem,
|
||||
boolean removeAudio,
|
||||
boolean removeVideo,
|
||||
boolean flattenForSlowMotion,
|
||||
EditedMediaItem editedMediaItem,
|
||||
MediaSource.Factory mediaSourceFactory,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
Looper looper,
|
||||
Listener listener,
|
||||
Clock clock) {
|
||||
this.mediaItem = mediaItem;
|
||||
this.editedMediaItem = editedMediaItem;
|
||||
this.decoderFactory = new CapturingDecoderFactory(decoderFactory);
|
||||
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
|
||||
@ -195,7 +157,11 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
new ExoPlayer.Builder(
|
||||
context,
|
||||
new RenderersFactoryImpl(
|
||||
removeAudio, removeVideo, flattenForSlowMotion, this.decoderFactory, listener))
|
||||
editedMediaItem.removeAudio,
|
||||
editedMediaItem.removeVideo,
|
||||
editedMediaItem.flattenForSlowMotion,
|
||||
this.decoderFactory,
|
||||
listener))
|
||||
.setMediaSourceFactory(mediaSourceFactory)
|
||||
.setTrackSelector(trackSelector)
|
||||
.setLoadControl(loadControl)
|
||||
@ -214,7 +180,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
player.setMediaItem(mediaItem);
|
||||
player.setMediaItem(editedMediaItem.mediaItem);
|
||||
player.prepare();
|
||||
progressState = PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
|
||||
}
|
||||
|
@ -136,11 +136,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
Looper internalLooper = internalHandlerThread.getLooper();
|
||||
ComponentListener componentListener = new ComponentListener(editedMediaItem, fallbackListener);
|
||||
assetLoader =
|
||||
assetLoaderFactory
|
||||
.setRemoveAudio(editedMediaItem.removeAudio)
|
||||
.setRemoveVideo(editedMediaItem.removeVideo)
|
||||
.setFlattenVideoForSlowMotion(editedMediaItem.flattenForSlowMotion)
|
||||
.createAssetLoader(editedMediaItem.mediaItem, internalLooper, componentListener);
|
||||
assetLoaderFactory.createAssetLoader(editedMediaItem, internalLooper, componentListener);
|
||||
samplePipelines = new ArrayList<>();
|
||||
muxerWrapper = new MuxerWrapper(outputPath, muxerFactory, componentListener);
|
||||
transformerConditionVariable = new ConditionVariable();
|
||||
|
@ -117,12 +117,10 @@ public class ExoPlayerAssetLoaderTest {
|
||||
Looper looper, AssetLoader.Listener listener, Clock clock) {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
Codec.DecoderFactory decoderFactory = new DefaultDecoderFactory(context);
|
||||
MediaItem mediaItem = MediaItem.fromUri("asset:///media/mp4/sample.mp4");
|
||||
EditedMediaItem editedMediaItem =
|
||||
new EditedMediaItem.Builder(MediaItem.fromUri("asset:///media/mp4/sample.mp4")).build();
|
||||
return new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock)
|
||||
.setRemoveAudio(false)
|
||||
.setRemoveVideo(false)
|
||||
.setFlattenVideoForSlowMotion(false)
|
||||
.createAssetLoader(mediaItem, looper, listener);
|
||||
.createAssetLoader(editedMediaItem, looper, listener);
|
||||
}
|
||||
|
||||
private static final class FakeSampleConsumer implements SampleConsumer {
|
||||
|
@ -1229,22 +1229,8 @@ public final class TransformerEndToEndTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetLoader.Factory setRemoveAudio(boolean removeAudio) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetLoader.Factory setRemoveVideo(boolean removeVideo) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetLoader.Factory setFlattenVideoForSlowMotion(boolean flattenVideoForSlowMotion) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetLoader createAssetLoader(MediaItem mediaItem, Looper looper, Listener listener) {
|
||||
public AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, Listener listener) {
|
||||
return new FakeAssetLoader(listener, supportedOutputTypes, sampleConsumerRef);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user