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
This commit is contained in:
sheenachhabra 2023-08-30 06:43:05 -07:00 committed by Copybara-Service
parent cf3fd1f4dd
commit b37e37aa3c
3 changed files with 29 additions and 21 deletions

View file

@ -71,7 +71,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private final String outputPath; private final String outputPath;
private final Muxer.Factory muxerFactory; private final Muxer.Factory muxerFactory;
private final Listener listener;
private final SparseArray<TrackInfo> trackTypeToInfo; private final SparseArray<TrackInfo> trackTypeToInfo;
private final ScheduledExecutorService abortScheduledExecutorService; private final ScheduledExecutorService abortScheduledExecutorService;
@ -80,6 +79,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private @C.TrackType int previousTrackType; private @C.TrackType int previousTrackType;
private long minTrackTimeUs; private long minTrackTimeUs;
private long maxEndedTrackTimeUs; private long maxEndedTrackTimeUs;
private @MonotonicNonNull Listener listener;
private @MonotonicNonNull ScheduledFuture<?> abortScheduledFuture; private @MonotonicNonNull ScheduledFuture<?> abortScheduledFuture;
private boolean isAborted; private boolean isAborted;
private @MonotonicNonNull Muxer muxer; private @MonotonicNonNull Muxer muxer;
@ -87,16 +87,25 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private volatile int additionalRotationDegrees; private volatile int additionalRotationDegrees;
private volatile int trackCount; 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.outputPath = outputPath;
this.muxerFactory = muxerFactory; this.muxerFactory = muxerFactory;
this.listener = listener;
trackTypeToInfo = new SparseArray<>(); trackTypeToInfo = new SparseArray<>();
previousTrackType = C.TRACK_TYPE_NONE; previousTrackType = C.TRACK_TYPE_NONE;
abortScheduledExecutorService = Util.newSingleThreadScheduledExecutor(TIMER_THREAD_NAME); abortScheduledExecutorService = Util.newSingleThreadScheduledExecutor(TIMER_THREAD_NAME);
} }
/**
* Sets a {@link MuxerWrapper.Listener}.
*
* <p>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} * Sets the clockwise rotation to add to the {@linkplain #addTrackFormat(Format) video track's}
* rotation, in degrees. * rotation, in degrees.
@ -266,9 +275,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
TrackInfo trackInfo = trackTypeToInfo.get(trackType); TrackInfo trackInfo = trackTypeToInfo.get(trackType);
maxEndedTrackTimeUs = max(maxEndedTrackTimeUs, trackInfo.timeUs); maxEndedTrackTimeUs = max(maxEndedTrackTimeUs, trackInfo.timeUs);
Listener listener = checkNotNull(this.listener);
listener.onTrackEnded( listener.onTrackEnded(
trackType, trackInfo.format, trackInfo.getAverageBitrate(), trackInfo.sampleCount); trackType, trackInfo.format, trackInfo.getAverageBitrate(), trackInfo.sampleCount);
if (trackType == C.TRACK_TYPE_VIDEO) { if (trackType == C.TRACK_TYPE_VIDEO) {
DebugTraceUtil.logEvent(DebugTraceUtil.EVENT_MUXER_TRACK_ENDED_VIDEO, trackInfo.timeUs); DebugTraceUtil.logEvent(DebugTraceUtil.EVENT_MUXER_TRACK_ENDED_VIDEO, trackInfo.timeUs);
} else if (trackType == C.TRACK_TYPE_AUDIO) { } else if (trackType == C.TRACK_TYPE_AUDIO) {
@ -345,14 +354,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return; return;
} }
isAborted = true; isAborted = true;
listener.onError( checkNotNull(listener)
ExportException.createForMuxer( .onError(
new IllegalStateException( ExportException.createForMuxer(
Util.formatInvariant( new IllegalStateException(
MUXER_TIMEOUT_ERROR_FORMAT_STRING, Util.formatInvariant(
maxDelayBetweenSamplesMs, MUXER_TIMEOUT_ERROR_FORMAT_STRING,
DebugTraceUtil.generateTraceSummary())), maxDelayBetweenSamplesMs,
ExportException.ERROR_CODE_MUXING_TIMEOUT)); DebugTraceUtil.generateTraceSummary())),
ExportException.ERROR_CODE_MUXING_TIMEOUT));
}, },
maxDelayBetweenSamplesMs, maxDelayBetweenSamplesMs,
MILLISECONDS); MILLISECONDS);

View file

@ -837,6 +837,7 @@ public final class Transformer {
verifyApplicationThread(); verifyApplicationThread();
checkState(transformerInternal == null, "There is already an export in progress."); checkState(transformerInternal == null, "There is already an export in progress.");
MuxerWrapper muxerWrapper = new MuxerWrapper(path, muxerFactory);
TransformerInternalListener transformerInternalListener = TransformerInternalListener transformerInternalListener =
new TransformerInternalListener(composition); new TransformerInternalListener(composition);
HandlerWrapper applicationHandler = clock.createHandler(looper, /* callback= */ null); HandlerWrapper applicationHandler = clock.createHandler(looper, /* callback= */ null);
@ -862,13 +863,12 @@ public final class Transformer {
new TransformerInternal( new TransformerInternal(
context, context,
composition, composition,
path,
transformationRequest, transformationRequest,
assetLoaderFactory, assetLoaderFactory,
audioMixerFactory, audioMixerFactory,
videoFrameProcessorFactory, videoFrameProcessorFactory,
encoderFactory, encoderFactory,
muxerFactory, muxerWrapper,
transformerInternalListener, transformerInternalListener,
fallbackListener, fallbackListener,
applicationHandler, applicationHandler,

View file

@ -128,17 +128,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private volatile boolean released; private volatile boolean released;
// Warning suppression is needed to assign the MuxerWrapper with "this" as listener. // Warning suppression is needed to assign the MuxerWrapper with "this" as listener.
@SuppressWarnings("assignment.type.incompatible") @SuppressWarnings({"argument.type.incompatible"})
public TransformerInternal( public TransformerInternal(
Context context, Context context,
Composition composition, Composition composition,
String outputPath,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
AssetLoader.Factory assetLoaderFactory, AssetLoader.Factory assetLoaderFactory,
AudioMixer.Factory audioMixerFactory, AudioMixer.Factory audioMixerFactory,
VideoFrameProcessor.Factory videoFrameProcessorFactory, VideoFrameProcessor.Factory videoFrameProcessorFactory,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
Muxer.Factory muxerFactory, MuxerWrapper muxerWrapper,
Listener listener, Listener listener,
FallbackListener fallbackListener, FallbackListener fallbackListener,
HandlerWrapper applicationHandler, HandlerWrapper applicationHandler,
@ -150,6 +149,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this.listener = listener; this.listener = listener;
this.applicationHandler = applicationHandler; this.applicationHandler = applicationHandler;
this.clock = clock; 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 = new HandlerThread("Transformer:Internal");
internalHandlerThread.start(); internalHandlerThread.start();
sequenceAssetLoaders = new ArrayList<>(); sequenceAssetLoaders = new ArrayList<>();
@ -192,10 +194,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
HandlerWrapper internalHandler = HandlerWrapper internalHandler =
clock.createHandler(internalLooper, /* callback= */ this::handleMessage); clock.createHandler(internalLooper, /* callback= */ this::handleMessage);
this.internalHandler = internalHandler; 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() { public void start() {