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 Muxer.Factory muxerFactory;
private final Listener listener;
private final SparseArray<TrackInfo> 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}.
*
* <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}
* 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);

View file

@ -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,

View file

@ -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() {