mirror of
https://github.com/samsonjs/media.git
synced 2026-03-25 09:25:53 +00:00
Move hdrMode from defaultAssetLoaderFactory constructors to create()
Plumbing hdrMode through the default asset loader factory via the constructor is problematic because it breaks API boundaries. It means there is another way to set hdrMode outside of Composition.java and TransformationRequest.java, which is error prone and cause problems if someone an app starts customizing the assetloaderfactory. It also means custom asset loaders can't receive this information without hacking around. The introduction of the composition-level settings class makes this approach easily extensible for other settings applied on the composition level but use in an individual asset level basis (e.g. ultraHDR support). PiperOrigin-RevId: 611466920
This commit is contained in:
parent
7395c9a159
commit
bcfad4b3b4
12 changed files with 74 additions and 58 deletions
|
|
@ -127,7 +127,6 @@ public class ForceEndOfStreamTest {
|
|||
new DefaultAssetLoaderFactory(
|
||||
context,
|
||||
new FrameDroppingDecoderFactory(context, MP4_ASSET_FRAME_COUNT, framesToSkip),
|
||||
Composition.HDR_MODE_KEEP_HDR,
|
||||
Clock.DEFAULT))
|
||||
.build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
|
|||
import androidx.media3.test.utils.FakeExtractorOutput;
|
||||
import androidx.media3.test.utils.FakeTrackOutput;
|
||||
import androidx.media3.test.utils.TestUtil;
|
||||
import androidx.media3.transformer.AssetLoader.CompositionSettings;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
|
@ -1340,7 +1341,10 @@ public class TransformerEndToEndTest {
|
|||
|
||||
@Override
|
||||
public TextureAssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, AssetLoader.Listener listener) {
|
||||
EditedMediaItem editedMediaItem,
|
||||
Looper looper,
|
||||
AssetLoader.Listener listener,
|
||||
CompositionSettings compositionSettings) {
|
||||
Format format = new Format.Builder().setWidth(width).setHeight(height).build();
|
||||
OnInputFrameProcessedListener frameProcessedListener =
|
||||
(texId, syncObject) -> {
|
||||
|
|
|
|||
|
|
@ -61,10 +61,14 @@ public interface AssetLoader {
|
|||
* been created.
|
||||
* @param listener The {@link Listener} on which the {@link AssetLoader} should notify of
|
||||
* events.
|
||||
* @param compositionSettings The {@link CompositionSettings}.
|
||||
* @return An {@link AssetLoader}.
|
||||
*/
|
||||
AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, Listener listener);
|
||||
EditedMediaItem editedMediaItem,
|
||||
Looper looper,
|
||||
Listener listener,
|
||||
CompositionSettings compositionSettings);
|
||||
}
|
||||
|
||||
/** A listener of {@link AssetLoader} events. */
|
||||
|
|
@ -133,6 +137,24 @@ public interface AssetLoader {
|
|||
void onError(ExportException exportException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customizations set on the {@linkplain Composition composition-level} that are needed when
|
||||
* {@linkplain AssetLoader loading} each of the {@linkplain EditedMediaItem individual assets} in
|
||||
* the {@link Composition}.
|
||||
*/
|
||||
/* package */ class CompositionSettings {
|
||||
|
||||
public final @Composition.HdrMode int hdrMode;
|
||||
|
||||
public CompositionSettings() {
|
||||
this.hdrMode = Composition.HDR_MODE_KEEP_HDR;
|
||||
}
|
||||
|
||||
public CompositionSettings(@Composition.HdrMode int hdrMode) {
|
||||
this.hdrMode = hdrMode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported output types of an asset loader. Possible flag values are {@link
|
||||
* #SUPPORTED_OUTPUT_TYPE_ENCODED} and {@link #SUPPORTED_OUTPUT_TYPE_DECODED}.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import androidx.media3.common.util.Util;
|
|||
import androidx.media3.datasource.DataSourceBitmapLoader;
|
||||
import androidx.media3.datasource.DefaultDataSource;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.transformer.AssetLoader.CompositionSettings;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import java.util.Objects;
|
||||
|
|
@ -46,7 +47,6 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
|||
|
||||
private final Context context;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final @Composition.HdrMode int hdrMode;
|
||||
private final Clock clock;
|
||||
private final MediaSource.@MonotonicNonNull Factory mediaSourceFactory;
|
||||
private final BitmapLoader bitmapLoader;
|
||||
|
|
@ -64,21 +64,13 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
|||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param hdrMode The {@link Composition.HdrMode} to apply. Only {@link
|
||||
* Composition#HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC} and {@link
|
||||
* Composition#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR} are applied in this
|
||||
* component.
|
||||
* @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
|
||||
* testing.
|
||||
*/
|
||||
public DefaultAssetLoaderFactory(
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
@Composition.HdrMode int hdrMode,
|
||||
Clock clock) {
|
||||
Context context, Codec.DecoderFactory decoderFactory, Clock clock) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.hdrMode = hdrMode;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = null;
|
||||
@Nullable BitmapFactory.Options options = null;
|
||||
|
|
@ -109,7 +101,6 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
|||
Context context, @Composition.HdrMode int hdrMode, BitmapLoader bitmapLoader) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.decoderFactory = new DefaultDecoderFactory(context);
|
||||
this.hdrMode = hdrMode;
|
||||
this.clock = Clock.DEFAULT;
|
||||
this.mediaSourceFactory = null;
|
||||
this.bitmapLoader = bitmapLoader;
|
||||
|
|
@ -121,9 +112,6 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
|||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param hdrMode The {@link Composition.HdrMode} to apply. Only {@link
|
||||
* Composition#HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC} and {@link
|
||||
* Composition#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR} are applied.
|
||||
* @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
|
||||
|
|
@ -133,13 +121,11 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
|||
public DefaultAssetLoaderFactory(
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
@Composition.HdrMode int hdrMode,
|
||||
Clock clock,
|
||||
MediaSource.Factory mediaSourceFactory,
|
||||
BitmapLoader bitmapLoader) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.hdrMode = hdrMode;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
this.bitmapLoader = bitmapLoader;
|
||||
|
|
@ -147,22 +133,26 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
|||
|
||||
@Override
|
||||
public AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, AssetLoader.Listener listener) {
|
||||
EditedMediaItem editedMediaItem,
|
||||
Looper looper,
|
||||
AssetLoader.Listener listener,
|
||||
CompositionSettings compositionSettings) {
|
||||
MediaItem mediaItem = editedMediaItem.mediaItem;
|
||||
if (isImage(mediaItem.localConfiguration)) {
|
||||
if (imageAssetLoaderFactory == null) {
|
||||
imageAssetLoaderFactory = new ImageAssetLoader.Factory(bitmapLoader);
|
||||
}
|
||||
return imageAssetLoaderFactory.createAssetLoader(editedMediaItem, looper, listener);
|
||||
return imageAssetLoaderFactory.createAssetLoader(
|
||||
editedMediaItem, looper, listener, compositionSettings);
|
||||
}
|
||||
if (exoPlayerAssetLoaderFactory == null) {
|
||||
exoPlayerAssetLoaderFactory =
|
||||
mediaSourceFactory != null
|
||||
? new ExoPlayerAssetLoader.Factory(
|
||||
context, decoderFactory, hdrMode, clock, mediaSourceFactory)
|
||||
: new ExoPlayerAssetLoader.Factory(context, decoderFactory, hdrMode, clock);
|
||||
? new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock, mediaSourceFactory)
|
||||
: new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock);
|
||||
}
|
||||
return exoPlayerAssetLoaderFactory.createAssetLoader(editedMediaItem, looper, listener);
|
||||
return exoPlayerAssetLoaderFactory.createAssetLoader(
|
||||
editedMediaItem, looper, listener, compositionSettings);
|
||||
}
|
||||
|
||||
private boolean isImage(@Nullable MediaItem.LocalConfiguration localConfiguration) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
|||
|
||||
private final Context context;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final @Composition.HdrMode int hdrMode;
|
||||
private final Clock clock;
|
||||
@Nullable private final MediaSource.Factory mediaSourceFactory;
|
||||
|
||||
|
|
@ -75,20 +74,12 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
|||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param hdrMode The {@link Composition.HdrMode} to apply. Only {@link
|
||||
* Composition#HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC} and {@link
|
||||
* Composition#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR} are applied.
|
||||
* @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
|
||||
* testing.
|
||||
*/
|
||||
public Factory(
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
@Composition.HdrMode int hdrMode,
|
||||
Clock clock) {
|
||||
public Factory(Context context, Codec.DecoderFactory decoderFactory, Clock clock) {
|
||||
this.context = context;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.hdrMode = hdrMode;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = null;
|
||||
}
|
||||
|
|
@ -99,9 +90,6 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
|||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param hdrMode The {@link Composition.HdrMode} to apply. Only {@link
|
||||
* Composition#HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC} and {@link
|
||||
* Composition#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR} are applied.
|
||||
* @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
|
||||
|
|
@ -110,19 +98,20 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
|||
public Factory(
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
@Composition.HdrMode int hdrMode,
|
||||
Clock clock,
|
||||
MediaSource.Factory mediaSourceFactory) {
|
||||
this.context = context;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.hdrMode = hdrMode;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, Listener listener) {
|
||||
EditedMediaItem editedMediaItem,
|
||||
Looper looper,
|
||||
Listener listener,
|
||||
CompositionSettings compositionSettings) {
|
||||
MediaSource.Factory mediaSourceFactory = this.mediaSourceFactory;
|
||||
if (mediaSourceFactory == null) {
|
||||
DefaultExtractorsFactory defaultExtractorsFactory = new DefaultExtractorsFactory();
|
||||
|
|
@ -136,7 +125,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
|||
editedMediaItem,
|
||||
mediaSourceFactory,
|
||||
decoderFactory,
|
||||
hdrMode,
|
||||
compositionSettings.hdrMode,
|
||||
looper,
|
||||
listener,
|
||||
clock);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,10 @@ public final class ImageAssetLoader implements AssetLoader {
|
|||
|
||||
@Override
|
||||
public AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, Listener listener) {
|
||||
EditedMediaItem editedMediaItem,
|
||||
Looper looper,
|
||||
Listener listener,
|
||||
CompositionSettings compositionSettings) {
|
||||
return new ImageAssetLoader(editedMediaItem, listener, bitmapLoader);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,9 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
private final boolean isLooping;
|
||||
private final boolean forceAudioTrack;
|
||||
private final AssetLoader.Factory assetLoaderFactory;
|
||||
private final HandlerWrapper handler;
|
||||
private final CompositionSettings compositionSettings;
|
||||
private final Listener sequenceAssetLoaderListener;
|
||||
private final HandlerWrapper handler;
|
||||
|
||||
/**
|
||||
* A mapping from track types to {@link SampleConsumer} instances.
|
||||
|
|
@ -102,13 +103,15 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
EditedMediaItemSequence sequence,
|
||||
boolean forceAudioTrack,
|
||||
AssetLoader.Factory assetLoaderFactory,
|
||||
Looper looper,
|
||||
CompositionSettings compositionSettings,
|
||||
Listener listener,
|
||||
Clock clock) {
|
||||
Clock clock,
|
||||
Looper looper) {
|
||||
editedMediaItems = sequence.editedMediaItems;
|
||||
isLooping = sequence.isLooping;
|
||||
this.forceAudioTrack = forceAudioTrack;
|
||||
this.assetLoaderFactory = assetLoaderFactory;
|
||||
this.compositionSettings = compositionSettings;
|
||||
sequenceAssetLoaderListener = listener;
|
||||
handler = clock.createHandler(looper, /* callback= */ null);
|
||||
sampleConsumersByTrackType = new HashMap<>();
|
||||
|
|
@ -120,7 +123,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
// constructor.
|
||||
@SuppressWarnings("nullness:argument.type.incompatible")
|
||||
AssetLoader currentAssetLoader =
|
||||
assetLoaderFactory.createAssetLoader(editedMediaItems.get(0), looper, /* listener= */ this);
|
||||
assetLoaderFactory.createAssetLoader(
|
||||
editedMediaItems.get(0), looper, /* listener= */ this, compositionSettings);
|
||||
this.currentAssetLoader = currentAssetLoader;
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +509,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
assetLoaderFactory.createAssetLoader(
|
||||
editedMediaItem,
|
||||
checkNotNull(Looper.myLooper()),
|
||||
/* listener= */ SequenceAssetLoader.this);
|
||||
/* listener= */ SequenceAssetLoader.this,
|
||||
compositionSettings);
|
||||
currentAssetLoader.start();
|
||||
} catch (RuntimeException e) {
|
||||
onError(
|
||||
|
|
|
|||
|
|
@ -1541,8 +1541,7 @@ public final class Transformer {
|
|||
AssetLoader.Factory assetLoaderFactory = this.assetLoaderFactory;
|
||||
if (useDefaultAssetLoaderFactory || assetLoaderFactory == null) {
|
||||
assetLoaderFactory =
|
||||
new DefaultAssetLoaderFactory(
|
||||
context, new DefaultDecoderFactory(context), transformationRequest.hdrMode, clock);
|
||||
new DefaultAssetLoaderFactory(context, new DefaultDecoderFactory(context), clock);
|
||||
}
|
||||
DebugTraceUtil.reset();
|
||||
transformerInternal =
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ import androidx.media3.common.VideoFrameProcessor;
|
|||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.transformer.AssetLoader.CompositionSettings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
|
@ -222,9 +223,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
sequence,
|
||||
composition.forceAudioTrack,
|
||||
assetLoaderFactory,
|
||||
internalLooper,
|
||||
new CompositionSettings(transformationRequest.hdrMode),
|
||||
sequenceAssetLoaderListener,
|
||||
clock));
|
||||
clock,
|
||||
internalLooper));
|
||||
if (!sequence.isLooping) {
|
||||
// All sequences have a non-final duration at this point, as the AssetLoaders haven't
|
||||
// started loading yet.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import androidx.media3.common.Format;
|
|||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.decoder.DecoderInputBuffer;
|
||||
import androidx.media3.transformer.AssetLoader.CompositionSettings;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import java.time.Duration;
|
||||
|
|
@ -143,9 +144,8 @@ public class ExoPlayerAssetLoaderTest {
|
|||
Codec.DecoderFactory decoderFactory = new DefaultDecoderFactory(context);
|
||||
EditedMediaItem editedMediaItem =
|
||||
new EditedMediaItem.Builder(MediaItem.fromUri("asset:///media/mp4/sample.mp4")).build();
|
||||
return new ExoPlayerAssetLoader.Factory(
|
||||
context, decoderFactory, Composition.HDR_MODE_KEEP_HDR, clock)
|
||||
.createAssetLoader(editedMediaItem, Looper.myLooper(), listener);
|
||||
return new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock)
|
||||
.createAssetLoader(editedMediaItem, Looper.myLooper(), listener, new CompositionSettings());
|
||||
}
|
||||
|
||||
private static final class FakeSampleConsumer implements SampleConsumer {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.media3.common.Format;
|
|||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.util.TimestampIterator;
|
||||
import androidx.media3.datasource.DataSourceBitmapLoader;
|
||||
import androidx.media3.transformer.AssetLoader.CompositionSettings;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import java.time.Duration;
|
||||
|
|
@ -122,7 +123,7 @@ public class ImageAssetLoaderTest {
|
|||
.build();
|
||||
return new ImageAssetLoader.Factory(
|
||||
new DataSourceBitmapLoader(ApplicationProvider.getApplicationContext()))
|
||||
.createAssetLoader(editedMediaItem, Looper.myLooper(), listener);
|
||||
.createAssetLoader(editedMediaItem, Looper.myLooper(), listener, new CompositionSettings());
|
||||
}
|
||||
|
||||
private static final class FakeSampleConsumer implements SampleConsumer {
|
||||
|
|
|
|||
|
|
@ -917,7 +917,6 @@ public final class MediaItemExportTest {
|
|||
new ExoPlayerAssetLoader.Factory(
|
||||
context,
|
||||
decoderFactory,
|
||||
Composition.HDR_MODE_KEEP_HDR,
|
||||
new FakeClock(/* isAutoAdvancing= */ true),
|
||||
mediaSourceFactory);
|
||||
CapturingMuxer.Factory muxerFactory =
|
||||
|
|
@ -1617,7 +1616,10 @@ public final class MediaItemExportTest {
|
|||
|
||||
@Override
|
||||
public AssetLoader createAssetLoader(
|
||||
EditedMediaItem editedMediaItem, Looper looper, Listener listener) {
|
||||
EditedMediaItem editedMediaItem,
|
||||
Looper looper,
|
||||
Listener listener,
|
||||
CompositionSettings compositionSettings) {
|
||||
return new FakeAssetLoader(listener, supportedOutputTypes, sampleConsumerRef);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue