Commit 27c239d6 by tonihei Committed by Ian Baker

Directly track playback queue in AnalyticsCollector.

We currently try to keep track of the playback queue (MediaPeriodQueue)
by listening to onMediaPeriodCreated/onMediaPeriodReleased events.
This approach has some problems:
 1. It's easily broken by custom MediaSources that don't report these
    events correctly.
 2. We need to make some assumptions about what the order of these
    events actually means. For example it is currently important that
    the playing period gets released last in MediaPeriodQueue.clear()
 3. We don't see batched events (like MediaPeriodQueue.clear()), so that
    it is impossible to keep the "last reading period" for example. This
    information is needed to correctly associate renderer errors to
    periods after the queue has been cleared.

All of these problems can be solved by directly tracking the queue.

This also makes the onMediaPeriodCreated/Released/ReadingStarted events
obsolete and they can be removed in a future change.

PiperOrigin-RevId: 319739993
parent b30e2b96
......@@ -173,7 +173,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
this.pauseAtEndOfWindow = pauseAtEndOfWindow;
this.eventHandler = eventHandler;
this.clock = clock;
this.queue = new MediaPeriodQueue();
this.queue = new MediaPeriodQueue(analyticsCollector, eventHandler);
backBufferDurationUs = loadControl.getBackBufferDurationUs();
retainBackBufferFromKeyframe = loadControl.retainBackBufferFromKeyframe();
......
......@@ -15,15 +15,18 @@
*/
package com.google.android.exoplayer2;
import android.os.Handler;
import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Player.RepeatMode;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.common.collect.ImmutableList;
/**
* Holds a queue of media periods, from the currently playing media period at the front to the
......@@ -41,6 +44,8 @@ import com.google.android.exoplayer2.util.Assertions;
private final Timeline.Period period;
private final Timeline.Window window;
@Nullable private final AnalyticsCollector analyticsCollector;
private final Handler analyticsCollectorHandler;
private long nextWindowSequenceNumber;
private @RepeatMode int repeatMode;
......@@ -52,8 +57,18 @@ import com.google.android.exoplayer2.util.Assertions;
@Nullable private Object oldFrontPeriodUid;
private long oldFrontPeriodWindowSequenceNumber;
/** Creates a new media period queue. */
public MediaPeriodQueue() {
/**
* Creates a new media period queue.
*
* @param analyticsCollector An optional {@link AnalyticsCollector} to be informed of queue
* changes.
* @param analyticsCollectorHandler The {@link Handler} to call {@link AnalyticsCollector} methods
* on.
*/
public MediaPeriodQueue(
@Nullable AnalyticsCollector analyticsCollector, Handler analyticsCollectorHandler) {
this.analyticsCollector = analyticsCollector;
this.analyticsCollectorHandler = analyticsCollectorHandler;
period = new Timeline.Period();
window = new Timeline.Window();
}
......@@ -168,6 +183,7 @@ import com.google.android.exoplayer2.util.Assertions;
oldFrontPeriodUid = null;
loading = newPeriodHolder;
length++;
notifyQueueUpdate();
return newPeriodHolder;
}
......@@ -203,6 +219,7 @@ import com.google.android.exoplayer2.util.Assertions;
public MediaPeriodHolder advanceReadingPeriod() {
Assertions.checkState(reading != null && reading.getNext() != null);
reading = reading.getNext();
notifyQueueUpdate();
return reading;
}
......@@ -228,6 +245,7 @@ import com.google.android.exoplayer2.util.Assertions;
oldFrontPeriodWindowSequenceNumber = playing.info.id.windowSequenceNumber;
}
playing = playing.getNext();
notifyQueueUpdate();
return playing;
}
......@@ -241,6 +259,9 @@ import com.google.android.exoplayer2.util.Assertions;
*/
public boolean removeAfter(MediaPeriodHolder mediaPeriodHolder) {
Assertions.checkState(mediaPeriodHolder != null);
if (mediaPeriodHolder.equals(loading)) {
return false;
}
boolean removedReading = false;
loading = mediaPeriodHolder;
while (mediaPeriodHolder.getNext() != null) {
......@@ -253,22 +274,27 @@ import com.google.android.exoplayer2.util.Assertions;
length--;
}
loading.setNext(null);
notifyQueueUpdate();
return removedReading;
}
/** Clears the queue. */
public void clear() {
MediaPeriodHolder front = playing;
if (front != null) {
oldFrontPeriodUid = front.uid;
oldFrontPeriodWindowSequenceNumber = front.info.id.windowSequenceNumber;
removeAfter(front);
if (length == 0) {
return;
}
MediaPeriodHolder front = Assertions.checkStateNotNull(playing);
oldFrontPeriodUid = front.uid;
oldFrontPeriodWindowSequenceNumber = front.info.id.windowSequenceNumber;
while (front != null) {
front.release();
front = front.getNext();
}
playing = null;
loading = null;
reading = null;
length = 0;
notifyQueueUpdate();
}
/**
......@@ -392,6 +418,20 @@ import com.google.android.exoplayer2.util.Assertions;
// Internal methods.
private void notifyQueueUpdate() {
if (analyticsCollector != null) {
ImmutableList.Builder<MediaPeriodId> builder = ImmutableList.builder();
@Nullable MediaPeriodHolder period = playing;
while (period != null) {
builder.add(period.info.id);
period = period.getNext();
}
@Nullable MediaPeriodId readingPeriodId = reading == null ? null : reading.info.id;
analyticsCollectorHandler.post(
() -> analyticsCollector.updateMediaPeriodQueueInfo(builder.build(), readingPeriodId));
}
}
/**
* Resolves the specified timeline period and position to a {@link MediaPeriodId} that should be
* played, returning an identifier for an ad group if one needs to be played before the specified
......
......@@ -74,7 +74,9 @@ public final class MediaPeriodQueueTest {
@Before
public void setUp() {
mediaPeriodQueue = new MediaPeriodQueue();
mediaPeriodQueue =
new MediaPeriodQueue(
/* analyticsCollector= */ null, Util.createHandlerForCurrentOrMainLooper());
mediaSourceList =
new MediaSourceList(
mock(MediaSourceList.MediaSourceListInfoRefreshListener.class),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment