parent
b1c954fa84
commit
d1d03189eb
@ -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,13 +233,12 @@ 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() {
|
||||||
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user