From b37e37aa3cd2e774726531fcc121588688c66827 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Wed, 30 Aug 2023 06:43:05 -0700 Subject: [PATCH] Pass MuxerWrapper to the TransformerInternal For pause/resume feature, same MuxerWrapper needs to be used across intermediate exports. So pass the MuxerWrapper from Transformer.java More specifically, when resume() is called 1. Create a MuxerWrapper and remux the previous video samples (Export 1). 2. User the same MuxerWrapper and start processing remaining video samples (Export 2). PiperOrigin-RevId: 561325867 --- .../media3/transformer/MuxerWrapper.java | 34 ++++++++++++------- .../media3/transformer/Transformer.java | 4 +-- .../transformer/TransformerInternal.java | 12 +++---- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java b/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java index 1da02e7b42..d8623d19e9 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java @@ -71,7 +71,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final String outputPath; private final Muxer.Factory muxerFactory; - private final Listener listener; private final SparseArray trackTypeToInfo; private final ScheduledExecutorService abortScheduledExecutorService; @@ -80,6 +79,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private @C.TrackType int previousTrackType; private long minTrackTimeUs; private long maxEndedTrackTimeUs; + private @MonotonicNonNull Listener listener; private @MonotonicNonNull ScheduledFuture abortScheduledFuture; private boolean isAborted; private @MonotonicNonNull Muxer muxer; @@ -87,16 +87,25 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private volatile int additionalRotationDegrees; private volatile int trackCount; - public MuxerWrapper(String outputPath, Muxer.Factory muxerFactory, Listener listener) { + /** Creates an instance. */ + public MuxerWrapper(String outputPath, Muxer.Factory muxerFactory) { this.outputPath = outputPath; this.muxerFactory = muxerFactory; - this.listener = listener; trackTypeToInfo = new SparseArray<>(); previousTrackType = C.TRACK_TYPE_NONE; abortScheduledExecutorService = Util.newSingleThreadScheduledExecutor(TIMER_THREAD_NAME); } + /** + * Sets a {@link MuxerWrapper.Listener}. + * + *

The {@link MuxerWrapper.Listener} must be set before calling any other methods. + */ + public void setListener(Listener listener) { + this.listener = listener; + } + /** * Sets the clockwise rotation to add to the {@linkplain #addTrackFormat(Format) video track's} * rotation, in degrees. @@ -266,9 +275,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; TrackInfo trackInfo = trackTypeToInfo.get(trackType); maxEndedTrackTimeUs = max(maxEndedTrackTimeUs, trackInfo.timeUs); + Listener listener = checkNotNull(this.listener); listener.onTrackEnded( trackType, trackInfo.format, trackInfo.getAverageBitrate(), trackInfo.sampleCount); - if (trackType == C.TRACK_TYPE_VIDEO) { DebugTraceUtil.logEvent(DebugTraceUtil.EVENT_MUXER_TRACK_ENDED_VIDEO, trackInfo.timeUs); } else if (trackType == C.TRACK_TYPE_AUDIO) { @@ -345,14 +354,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; return; } isAborted = true; - listener.onError( - ExportException.createForMuxer( - new IllegalStateException( - Util.formatInvariant( - MUXER_TIMEOUT_ERROR_FORMAT_STRING, - maxDelayBetweenSamplesMs, - DebugTraceUtil.generateTraceSummary())), - ExportException.ERROR_CODE_MUXING_TIMEOUT)); + checkNotNull(listener) + .onError( + ExportException.createForMuxer( + new IllegalStateException( + Util.formatInvariant( + MUXER_TIMEOUT_ERROR_FORMAT_STRING, + maxDelayBetweenSamplesMs, + DebugTraceUtil.generateTraceSummary())), + ExportException.ERROR_CODE_MUXING_TIMEOUT)); }, maxDelayBetweenSamplesMs, MILLISECONDS); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index b6d4056309..9dea16e175 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -837,6 +837,7 @@ public final class Transformer { verifyApplicationThread(); checkState(transformerInternal == null, "There is already an export in progress."); + MuxerWrapper muxerWrapper = new MuxerWrapper(path, muxerFactory); TransformerInternalListener transformerInternalListener = new TransformerInternalListener(composition); HandlerWrapper applicationHandler = clock.createHandler(looper, /* callback= */ null); @@ -862,13 +863,12 @@ public final class Transformer { new TransformerInternal( context, composition, - path, transformationRequest, assetLoaderFactory, audioMixerFactory, videoFrameProcessorFactory, encoderFactory, - muxerFactory, + muxerWrapper, transformerInternalListener, fallbackListener, applicationHandler, diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java index 2e30d6fc24..fa1dce1e0c 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java @@ -128,17 +128,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private volatile boolean released; // Warning suppression is needed to assign the MuxerWrapper with "this" as listener. - @SuppressWarnings("assignment.type.incompatible") + @SuppressWarnings({"argument.type.incompatible"}) public TransformerInternal( Context context, Composition composition, - String outputPath, TransformationRequest transformationRequest, AssetLoader.Factory assetLoaderFactory, AudioMixer.Factory audioMixerFactory, VideoFrameProcessor.Factory videoFrameProcessorFactory, Codec.EncoderFactory encoderFactory, - Muxer.Factory muxerFactory, + MuxerWrapper muxerWrapper, Listener listener, FallbackListener fallbackListener, HandlerWrapper applicationHandler, @@ -150,6 +149,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.listener = listener; this.applicationHandler = applicationHandler; this.clock = clock; + this.muxerWrapper = muxerWrapper; + // It's safe to use "this" because we don't mux any data before exiting the constructor. + this.muxerWrapper.setListener(this); internalHandlerThread = new HandlerThread("Transformer:Internal"); internalHandlerThread.start(); sequenceAssetLoaders = new ArrayList<>(); @@ -192,10 +194,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; HandlerWrapper internalHandler = clock.createHandler(internalLooper, /* callback= */ this::handleMessage); this.internalHandler = internalHandler; - // It's safe to use "this" because we don't mux any data before exiting the constructor. - @SuppressWarnings("nullness:argument.type.incompatible") - MuxerWrapper muxerWrapper = new MuxerWrapper(outputPath, muxerFactory, /* listener= */ this); - this.muxerWrapper = muxerWrapper; } public void start() {