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