PiperOrigin-RevId: 600393114
This commit is contained in:
christosts 2024-01-22 02:16:22 -08:00 committed by Copybara-Service
parent b1c954fa84
commit d1d03189eb

View File

@ -41,7 +41,6 @@ import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.DebugViewProvider;
@ -101,6 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private static final int MSG_REGISTER_SAMPLE_EXPORTER = 1;
private static final int MSG_DRAIN_EXPORTERS = 2;
private static final int MSG_END = 3;
private static final int MSG_UPDATE_PROGRESS = 4;
private static final String TAG = "TransformerInternal";
private static final int DRAIN_EXPORTERS_DELAY_MS = 10;
@ -129,9 +129,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final List<SampleExporter> sampleExporters;
private final MuxerWrapper muxerWrapper;
private final ConditionVariable transformerCancelConditionVariable;
private final ConditionVariable transformerConditionVariable;
private final Object setMaxSequenceDurationUsLock;
private final Object progressLock;
private boolean isDrainingExporters;
@ -146,21 +145,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* The current {@link Transformer.ProgressState}.
*
* <p>Modified on the internal thread. Accessed on internal thread and application thread (in
* {@link #getProgress}).
* <p>Modified on the internal thread. Accessed on the application thread (in {@link
* #getProgress}).
*/
@GuardedBy("progressLock")
private @Transformer.ProgressState int progressState;
/**
* The current progress value, from 0 to 100.
*
* <p>Modified on the internal thread. Accessed on internal thread and application thread (in
* {@link #getProgress}).
*/
@GuardedBy("progressLock")
@IntRange(from = 0, to = 100)
private int progressValue;
private volatile @Transformer.ProgressState int progressState;
/**
* The boolean tracking if this component has been released.
@ -227,10 +215,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
compositionHasLoopingSequence =
nonLoopingSequencesWithNonFinalDuration != composition.sequences.size();
setMaxSequenceDurationUsLock = new Object();
transformerCancelConditionVariable = new ConditionVariable();
progressLock = new Object();
sampleExporters = new ArrayList<>();
transformerConditionVariable = new ConditionVariable();
// It's safe to use "this" because we don't send a message before exiting the constructor.
@SuppressWarnings("nullness:methodref.receiver.bound")
HandlerWrapper internalHandler =
@ -247,14 +233,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (released) {
return PROGRESS_STATE_NOT_STARTED;
}
synchronized (progressLock) {
if (progressState == PROGRESS_STATE_AVAILABLE) {
progressHolder.progress = progressValue;
}
verifyInternalThreadAlive();
internalHandler.obtainMessage(MSG_UPDATE_PROGRESS, progressHolder).sendToTarget();
// TODO: figure out why calling clock.onThreadBlocked() here makes the tests fail.
transformerConditionVariable.blockUninterruptible();
transformerConditionVariable.close();
return progressState;
}
}
public void cancel() {
if (released) {
@ -265,8 +250,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
.obtainMessage(MSG_END, END_REASON_CANCELLED, /* unused */ 0, /* exportException */ null)
.sendToTarget();
clock.onThreadBlocked();
transformerCancelConditionVariable.blockUninterruptible();
transformerCancelConditionVariable.close();
transformerConditionVariable.blockUninterruptible();
transformerConditionVariable.close();
if (cancelException != null) {
throw cancelException;
}
@ -297,7 +282,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// handled to report release timeouts and to unblock the transformer condition variable in case
// of cancellation. Progress update messages must be handled to unblock the transformer
// condition variable.
if (released && msg.what != MSG_END) {
if (released && msg.what != MSG_END && msg.what != MSG_UPDATE_PROGRESS) {
return true;
}
try {
@ -314,6 +299,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
case MSG_END:
endInternal(/* endReason= */ msg.arg1, /* exportException= */ (ExportException) msg.obj);
break;
case MSG_UPDATE_PROGRESS:
updateProgressInternal(/* progressHolder= */ (ProgressHolder) msg.obj);
break;
default:
return false;
}
@ -344,8 +332,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
while (sampleExporters.get(i).processData()) {}
}
updateProgressInternal();
if (!muxerWrapper.isEnded()) {
internalHandler.sendEmptyMessageDelayed(MSG_DRAIN_EXPORTERS, DRAIN_EXPORTERS_DELAY_MS);
}
@ -363,11 +349,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
boolean releasedPreviously = released;
if (!released) {
released = true;
synchronized (progressLock) {
progressState = PROGRESS_STATE_NOT_STARTED;
progressValue = 0;
}
// VideoSampleExporter can hold buffers from the asset loader's decoder in a surface texture,
// so we release the VideoSampleExporter first to avoid releasing the codec while its buffers
// are pending processing.
@ -410,8 +391,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
internalHandler.post(internalHandlerThread::quitSafely);
}
// Update progress before opening variable to avoid getProgress returning an invalid combination
// of state and progress.
progressState = PROGRESS_STATE_NOT_STARTED;
transformerConditionVariable.open();
if (forCancellation) {
transformerCancelConditionVariable.open();
return;
}
@ -452,50 +437,25 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
/**
* Calculates the current progress of the export, updating the thread-safe class variables.
*
* <p>Should only be called on the internal thread.
*/
private void updateProgressInternal() {
if (released) {
return;
}
synchronized (progressLock) {
if (progressState == PROGRESS_STATE_NOT_STARTED) {
return;
}
}
@Transformer.ProgressState int individualProgressState;
ProgressHolder individualProgressHolder = new ProgressHolder();
private void updateProgressInternal(ProgressHolder progressHolder) {
int progressSum = 0;
int progressCount = 0;
ProgressHolder individualProgressHolder = new ProgressHolder();
for (int i = 0; i < sequenceAssetLoaders.size(); i++) {
if (composition.sequences.get(i).isLooping) {
// Looping sequence progress is always unavailable. Skip it.
continue;
}
individualProgressHolder.progress = 0;
individualProgressState = sequenceAssetLoaders.get(i).getProgress(individualProgressHolder);
if (individualProgressState != PROGRESS_STATE_AVAILABLE) {
synchronized (progressLock) {
progressState = individualProgressState;
progressValue = 0;
}
progressState = sequenceAssetLoaders.get(i).getProgress(individualProgressHolder);
if (progressState != PROGRESS_STATE_AVAILABLE) {
transformerConditionVariable.open();
return;
}
progressSum += individualProgressHolder.progress;
progressCount++;
}
synchronized (progressLock) {
progressState = PROGRESS_STATE_AVAILABLE;
progressValue = progressSum / progressCount;
}
progressHolder.progress = progressSum / progressCount;
transformerConditionVariable.open();
}
private final class SequenceAssetLoaderListener implements AssetLoader.Listener {