mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Limit maximum number of parallel metadata retrievals.
The operation almost always acquires resources straight-away, for example a new thread or file access. This means starting many metadata retrievals in parallel easily causes resource contention. This problem can be alleviated by limiting the maximum number of parallel retrievals. Local testing showed a 20% speedup for local file retrievals when limited to 5 in parallel. Any more parallel retrievals did not show any improvement or started getting slower again the more operations are allowed. PiperOrigin-RevId: 650674685
This commit is contained in:
parent
5777e30979
commit
b145a9b35e
@ -16,6 +16,7 @@
|
||||
|
||||
package androidx.media3.exoplayer;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
|
||||
@ -24,6 +25,7 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.C;
|
||||
@ -44,12 +46,18 @@ import androidx.media3.extractor.ExtractorsFactory;
|
||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** Retrieves the static metadata of {@link MediaItem MediaItems}. */
|
||||
@UnstableApi
|
||||
public final class MetadataRetriever {
|
||||
|
||||
/** The default number of maximum parallel retrievals. */
|
||||
public static final int DEFAULT_MAXIMUM_PARALLEL_RETRIEVALS = 5;
|
||||
|
||||
private MetadataRetriever() {}
|
||||
|
||||
/**
|
||||
@ -97,7 +105,19 @@ public final class MetadataRetriever {
|
||||
|
||||
private static ListenableFuture<TrackGroupArray> retrieveMetadata(
|
||||
MediaSource.Factory mediaSourceFactory, MediaItem mediaItem, Clock clock) {
|
||||
return new MetadataRetrieverInternal(mediaSourceFactory, clock).retrieveMetadata(mediaItem);
|
||||
return new MetadataRetrieverInternal(mediaSourceFactory, mediaItem, clock).retrieveMetadata();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of metadata retrievals run in parallel.
|
||||
*
|
||||
* <p>The default is {@link #DEFAULT_MAXIMUM_PARALLEL_RETRIEVALS}.
|
||||
*
|
||||
* @param maximumParallelRetrievals The maximum number of parallel retrievals.
|
||||
*/
|
||||
public static void setMaximumParallelRetrievals(int maximumParallelRetrievals) {
|
||||
checkArgument(maximumParallelRetrievals >= 1);
|
||||
SharedWorkerThread.MAX_PARALLEL_RETRIEVALS.set(maximumParallelRetrievals);
|
||||
}
|
||||
|
||||
private static final class MetadataRetrieverInternal {
|
||||
@ -110,22 +130,29 @@ public final class MetadataRetriever {
|
||||
private static final SharedWorkerThread SHARED_WORKER_THREAD = new SharedWorkerThread();
|
||||
|
||||
private final MediaSource.Factory mediaSourceFactory;
|
||||
private final MediaItem mediaItem;
|
||||
private final HandlerWrapper mediaSourceHandler;
|
||||
private final SettableFuture<TrackGroupArray> trackGroupsFuture;
|
||||
|
||||
public MetadataRetrieverInternal(MediaSource.Factory mediaSourceFactory, Clock clock) {
|
||||
public MetadataRetrieverInternal(
|
||||
MediaSource.Factory mediaSourceFactory, MediaItem mediaItem, Clock clock) {
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
this.mediaItem = mediaItem;
|
||||
Looper workerThreadLooper = SHARED_WORKER_THREAD.addWorker();
|
||||
mediaSourceHandler =
|
||||
clock.createHandler(workerThreadLooper, new MediaSourceHandlerCallback());
|
||||
trackGroupsFuture = SettableFuture.create();
|
||||
}
|
||||
|
||||
public ListenableFuture<TrackGroupArray> retrieveMetadata(MediaItem mediaItem) {
|
||||
mediaSourceHandler.obtainMessage(MESSAGE_PREPARE_SOURCE, mediaItem).sendToTarget();
|
||||
public ListenableFuture<TrackGroupArray> retrieveMetadata() {
|
||||
SHARED_WORKER_THREAD.startRetrieval(this);
|
||||
return trackGroupsFuture;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
mediaSourceHandler.obtainMessage(MESSAGE_PREPARE_SOURCE, mediaItem).sendToTarget();
|
||||
}
|
||||
|
||||
private final class MediaSourceHandlerCallback implements Handler.Callback {
|
||||
|
||||
private static final int ERROR_POLL_INTERVAL_MS = 100;
|
||||
@ -229,9 +256,18 @@ public final class MetadataRetriever {
|
||||
|
||||
private static final class SharedWorkerThread {
|
||||
|
||||
public static final AtomicInteger MAX_PARALLEL_RETRIEVALS =
|
||||
new AtomicInteger(DEFAULT_MAXIMUM_PARALLEL_RETRIEVALS);
|
||||
|
||||
private final Deque<MetadataRetrieverInternal> pendingRetrievals;
|
||||
|
||||
@Nullable private HandlerThread mediaSourceThread;
|
||||
private int referenceCount;
|
||||
|
||||
public SharedWorkerThread() {
|
||||
pendingRetrievals = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
public synchronized Looper addWorker() {
|
||||
if (mediaSourceThread == null) {
|
||||
checkState(referenceCount == 0);
|
||||
@ -242,10 +278,29 @@ public final class MetadataRetriever {
|
||||
return mediaSourceThread.getLooper();
|
||||
}
|
||||
|
||||
public synchronized void startRetrieval(MetadataRetrieverInternal retrieval) {
|
||||
pendingRetrievals.addLast(retrieval);
|
||||
maybeStartNewRetrieval();
|
||||
}
|
||||
|
||||
public synchronized void removeWorker() {
|
||||
if (--referenceCount == 0) {
|
||||
checkNotNull(mediaSourceThread).quit();
|
||||
mediaSourceThread = null;
|
||||
} else {
|
||||
maybeStartNewRetrieval();
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void maybeStartNewRetrieval() {
|
||||
if (pendingRetrievals.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int activeRetrievals = referenceCount - pendingRetrievals.size();
|
||||
if (activeRetrievals < MAX_PARALLEL_RETRIEVALS.get()) {
|
||||
MetadataRetrieverInternal retrieval = pendingRetrievals.removeFirst();
|
||||
retrieval.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user