diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FrameProcessingTaskExecutor.java b/libraries/effect/src/main/java/androidx/media3/effect/FrameProcessingTaskExecutor.java index a87356e965..d5e08c7d95 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FrameProcessingTaskExecutor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FrameProcessingTaskExecutor.java @@ -34,12 +34,17 @@ import java.util.concurrent.atomic.AtomicBoolean; *
The wrapper handles calling {@link * FrameProcessor.Listener#onFrameProcessingError(FrameProcessingException)} for errors that occur * during these tasks. + * + *
{@linkplain #submitWithHighPriority(FrameProcessingTask) High priority tasks} are always
+ * executed before {@linkplain #submit(FrameProcessingTask) default priority tasks}. Tasks with
+ * equal priority are executed in FIFO order.
*/
/* package */ final class FrameProcessingTaskExecutor {
private final ExecutorService singleThreadExecutorService;
private final FrameProcessor.Listener listener;
private final ConcurrentLinkedQueue Tasks that were previously {@linkplain #submit(FrameProcessingTask) submitted} without
+ * high-priority and have not started executing will be executed after this task is complete.
+ */
+ public void submitWithHighPriority(FrameProcessingTask task) {
+ if (shouldCancelTasks.get()) {
+ return;
+ }
+ highPriorityTasks.add(task);
+ // If the ExecutorService has non-started tasks, the first of these non-started tasks will run
+ // the task passed to this method. Just in case there are no non-started tasks, submit another
+ // task to run high-priority tasks.
+ submit(() -> {});
+ }
+
/**
* Cancels remaining tasks, runs the given release task, and shuts down the background thread.
*
@@ -83,7 +107,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
while (!futures.isEmpty()) {
futures.remove().cancel(/* mayInterruptIfRunning= */ false);
}
- Future> releaseFuture = submitTask(releaseTask);
+ Future> releaseFuture = wrapTaskAndSubmitToExecutorService(releaseTask);
singleThreadExecutorService.shutdown();
try {
if (!singleThreadExecutorService.awaitTermination(releaseWaitTimeMs, MILLISECONDS)) {
@@ -95,11 +119,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- private Future> submitTask(FrameProcessingTask glTask) {
+ private Future> wrapTaskAndSubmitToExecutorService(FrameProcessingTask defaultPriorityTask) {
return singleThreadExecutorService.submit(
() -> {
try {
- glTask.run();
+ while (!highPriorityTasks.isEmpty()) {
+ highPriorityTasks.remove().run();
+ }
+ defaultPriorityTask.run();
removeFinishedFutures();
} catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) {
listener.onFrameProcessingError(FrameProcessingException.from(e));