mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
Restructure media period queue tracker in AnalyticsCollector.
The current structure tries to associate events to media periods and windows based on the reported values and the current timeline. However the reported EventTime may not always be consistent in case the timeline doesn't contain windows or media periods yet or not anymore. The recent changes to MediaPeriodId allow to use it as a unique identifer for media periods independent of the timeline. This enables more accurate tracking of the media period queue and prevents reporting events with inconsistent data. Issue:#4492 Issue:#4634 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=210713120
This commit is contained in:
parent
ca0e276798
commit
d308fff88d
@ -133,6 +133,9 @@
|
||||
* Provide additional information for adaptive track selection.
|
||||
`TrackSelection.updateSelectedTrack` has two new parameters for the current
|
||||
queue of media chunks and iterators for information about upcoming chunks.
|
||||
* Prevent potential issues when reporting events for multi-period media sources
|
||||
([#4492](https://github.com/google/ExoPlayer/issues/4492) and
|
||||
[#4634](https://github.com/google/ExoPlayer/issues/4634)).
|
||||
|
||||
### 2.8.4 ###
|
||||
|
||||
|
@ -45,10 +45,12 @@ import com.google.android.exoplayer2.video.VideoRendererEventListener;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|
||||
/**
|
||||
* Data collector which is able to forward analytics events to {@link AnalyticsListener}s by
|
||||
@ -98,7 +100,9 @@ public class AnalyticsCollector
|
||||
* @param clock A {@link Clock} used to generate timestamps.
|
||||
*/
|
||||
protected AnalyticsCollector(@Nullable Player player, Clock clock) {
|
||||
this.player = player;
|
||||
if (player != null) {
|
||||
this.player = player;
|
||||
}
|
||||
this.clock = Assertions.checkNotNull(clock);
|
||||
listeners = new CopyOnWriteArraySet<>();
|
||||
mediaPeriodQueueTracker = new MediaPeriodQueueTracker();
|
||||
@ -156,10 +160,10 @@ public class AnalyticsCollector
|
||||
*/
|
||||
public final void resetForNewMediaSource() {
|
||||
// Copying the list is needed because onMediaPeriodReleased will modify the list.
|
||||
List<WindowAndMediaPeriodId> activeMediaPeriods =
|
||||
new ArrayList<>(mediaPeriodQueueTracker.activeMediaPeriods);
|
||||
for (WindowAndMediaPeriodId mediaPeriod : activeMediaPeriods) {
|
||||
onMediaPeriodReleased(mediaPeriod.windowIndex, mediaPeriod.mediaPeriodId);
|
||||
List<MediaPeriodInfo> mediaPeriodInfos =
|
||||
new ArrayList<>(mediaPeriodQueueTracker.mediaPeriodInfoQueue);
|
||||
for (MediaPeriodInfo mediaPeriodInfo : mediaPeriodInfos) {
|
||||
onMediaPeriodReleased(mediaPeriodInfo.windowIndex, mediaPeriodInfo.mediaPeriodId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +336,7 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
mediaPeriodQueueTracker.onMediaPeriodCreated(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onMediaPeriodCreated(eventTime);
|
||||
}
|
||||
@ -340,10 +344,11 @@ public class AnalyticsCollector
|
||||
|
||||
@Override
|
||||
public final void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
mediaPeriodQueueTracker.onMediaPeriodReleased(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onMediaPeriodReleased(eventTime);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
if (mediaPeriodQueueTracker.onMediaPeriodReleased(mediaPeriodId)) {
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onMediaPeriodReleased(eventTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,7 +358,7 @@ public class AnalyticsCollector
|
||||
@Nullable MediaPeriodId mediaPeriodId,
|
||||
LoadEventInfo loadEventInfo,
|
||||
MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData);
|
||||
}
|
||||
@ -365,7 +370,7 @@ public class AnalyticsCollector
|
||||
@Nullable MediaPeriodId mediaPeriodId,
|
||||
LoadEventInfo loadEventInfo,
|
||||
MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onLoadCompleted(eventTime, loadEventInfo, mediaLoadData);
|
||||
}
|
||||
@ -377,7 +382,7 @@ public class AnalyticsCollector
|
||||
@Nullable MediaPeriodId mediaPeriodId,
|
||||
LoadEventInfo loadEventInfo,
|
||||
MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onLoadCanceled(eventTime, loadEventInfo, mediaLoadData);
|
||||
}
|
||||
@ -391,7 +396,7 @@ public class AnalyticsCollector
|
||||
MediaLoadData mediaLoadData,
|
||||
IOException error,
|
||||
boolean wasCanceled) {
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onLoadError(eventTime, loadEventInfo, mediaLoadData, error, wasCanceled);
|
||||
}
|
||||
@ -399,8 +404,8 @@ public class AnalyticsCollector
|
||||
|
||||
@Override
|
||||
public final void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
mediaPeriodQueueTracker.onReadingStarted(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
mediaPeriodQueueTracker.onReadingStarted(mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onReadingStarted(eventTime);
|
||||
}
|
||||
@ -409,7 +414,7 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onUpstreamDiscarded(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onUpstreamDiscarded(eventTime, mediaLoadData);
|
||||
}
|
||||
@ -418,7 +423,7 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onDownstreamFormatChanged(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateEventTime(windowIndex, mediaPeriodId);
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onDownstreamFormatChanged(eventTime, mediaLoadData);
|
||||
}
|
||||
@ -568,32 +573,32 @@ public class AnalyticsCollector
|
||||
return Collections.unmodifiableSet(listeners);
|
||||
}
|
||||
|
||||
/** Returns a new {@link EventTime} for the specified window index and media period id. */
|
||||
protected EventTime generateEventTime(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
Assertions.checkNotNull(player);
|
||||
/** Returns a new {@link EventTime} for the specified timeline, window and media period id. */
|
||||
@RequiresNonNull("player")
|
||||
protected EventTime generateEventTime(
|
||||
Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
if (timeline.isEmpty()) {
|
||||
// Ensure media period id is only reported together with a valid timeline.
|
||||
mediaPeriodId = null;
|
||||
}
|
||||
long realtimeMs = clock.elapsedRealtime();
|
||||
Timeline timeline = player.getCurrentTimeline();
|
||||
long eventPositionMs;
|
||||
if (windowIndex == player.getCurrentWindowIndex()) {
|
||||
if (mediaPeriodId != null && mediaPeriodId.isAd()) {
|
||||
// This event is for an ad in the currently playing window.
|
||||
eventPositionMs =
|
||||
player.getCurrentAdGroupIndex() == mediaPeriodId.adGroupIndex
|
||||
&& player.getCurrentAdIndexInAdGroup() == mediaPeriodId.adIndexInAdGroup
|
||||
? player.getCurrentPosition()
|
||||
: 0 /* Assume start position of 0 for a future ad. */;
|
||||
} else {
|
||||
// This event is for content in the currently playing window.
|
||||
eventPositionMs = player.getContentPosition();
|
||||
}
|
||||
} else if (windowIndex >= timeline.getWindowCount()
|
||||
|| (mediaPeriodId != null && mediaPeriodId.isAd())) {
|
||||
// This event is for an unknown future window or for an ad in a future window.
|
||||
// Assume start position of zero.
|
||||
eventPositionMs = 0;
|
||||
boolean isInCurrentWindow =
|
||||
timeline == player.getCurrentTimeline() && windowIndex == player.getCurrentWindowIndex();
|
||||
if (mediaPeriodId != null && mediaPeriodId.isAd()) {
|
||||
boolean isCurrentAd =
|
||||
isInCurrentWindow
|
||||
&& player.getCurrentAdGroupIndex() == mediaPeriodId.adGroupIndex
|
||||
&& player.getCurrentAdIndexInAdGroup() == mediaPeriodId.adIndexInAdGroup;
|
||||
// Assume start position of 0 for future ads.
|
||||
eventPositionMs = isCurrentAd ? player.getCurrentPosition() : 0;
|
||||
} else if (isInCurrentWindow) {
|
||||
eventPositionMs = player.getContentPosition();
|
||||
} else {
|
||||
// This event is for content in a future window. Assume default start position.
|
||||
eventPositionMs = timeline.getWindow(windowIndex, window).getDefaultPositionMs();
|
||||
// Assume default start position for future content windows. If timeline is not available yet,
|
||||
// assume start position of 0.
|
||||
eventPositionMs =
|
||||
timeline.isEmpty() ? 0 : timeline.getWindow(windowIndex, window).getDefaultPositionMs();
|
||||
}
|
||||
return new EventTime(
|
||||
realtimeMs,
|
||||
@ -605,13 +610,20 @@ public class AnalyticsCollector
|
||||
player.getTotalBufferedDuration());
|
||||
}
|
||||
|
||||
private EventTime generateEventTime(@Nullable WindowAndMediaPeriodId mediaPeriod) {
|
||||
if (mediaPeriod == null) {
|
||||
int windowIndex = Assertions.checkNotNull(player).getCurrentWindowIndex();
|
||||
MediaPeriodId mediaPeriodId = mediaPeriodQueueTracker.tryResolveWindowIndex(windowIndex);
|
||||
return generateEventTime(windowIndex, mediaPeriodId);
|
||||
private EventTime generateEventTime(@Nullable MediaPeriodInfo mediaPeriodInfo) {
|
||||
Assertions.checkNotNull(player);
|
||||
if (mediaPeriodInfo == null) {
|
||||
int windowIndex = player.getCurrentWindowIndex();
|
||||
mediaPeriodInfo = mediaPeriodQueueTracker.tryResolveWindowIndex(windowIndex);
|
||||
if (mediaPeriodInfo == null) {
|
||||
Timeline timeline = player.getCurrentTimeline();
|
||||
boolean windowIsInTimeline = windowIndex < timeline.getWindowCount();
|
||||
return generateEventTime(
|
||||
windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null);
|
||||
}
|
||||
}
|
||||
return generateEventTime(mediaPeriod.windowIndex, mediaPeriod.mediaPeriodId);
|
||||
return generateEventTime(
|
||||
mediaPeriodInfo.timeline, mediaPeriodInfo.windowIndex, mediaPeriodInfo.mediaPeriodId);
|
||||
}
|
||||
|
||||
private EventTime generateLastReportedPlayingMediaPeriodEventTime() {
|
||||
@ -630,67 +642,89 @@ public class AnalyticsCollector
|
||||
return generateEventTime(mediaPeriodQueueTracker.getLoadingMediaPeriod());
|
||||
}
|
||||
|
||||
private EventTime generateMediaPeriodEventTime(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
Assertions.checkNotNull(player);
|
||||
if (mediaPeriodId != null) {
|
||||
MediaPeriodInfo mediaPeriodInfo = mediaPeriodQueueTracker.getMediaPeriodInfo(mediaPeriodId);
|
||||
return mediaPeriodInfo != null
|
||||
? generateEventTime(mediaPeriodInfo)
|
||||
: generateEventTime(Timeline.EMPTY, windowIndex, mediaPeriodId);
|
||||
}
|
||||
Timeline timeline = player.getCurrentTimeline();
|
||||
boolean windowIsInTimeline = windowIndex < timeline.getWindowCount();
|
||||
return generateEventTime(
|
||||
windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null);
|
||||
}
|
||||
|
||||
/** Keeps track of the active media periods and currently playing and reading media period. */
|
||||
private static final class MediaPeriodQueueTracker {
|
||||
|
||||
// TODO: Investigate reporting MediaPeriodId in renderer events and adding a listener of queue
|
||||
// changes, which would hopefully remove the need to track the queue here.
|
||||
|
||||
private final ArrayList<WindowAndMediaPeriodId> activeMediaPeriods;
|
||||
private final ArrayList<MediaPeriodInfo> mediaPeriodInfoQueue;
|
||||
private final HashMap<MediaPeriodId, MediaPeriodInfo> mediaPeriodIdToInfo;
|
||||
private final Period period;
|
||||
|
||||
private WindowAndMediaPeriodId lastReportedPlayingMediaPeriod;
|
||||
private WindowAndMediaPeriodId readingMediaPeriod;
|
||||
private @Nullable MediaPeriodInfo lastReportedPlayingMediaPeriod;
|
||||
private @Nullable MediaPeriodInfo readingMediaPeriod;
|
||||
private Timeline timeline;
|
||||
private boolean isSeeking;
|
||||
|
||||
public MediaPeriodQueueTracker() {
|
||||
activeMediaPeriods = new ArrayList<>();
|
||||
mediaPeriodInfoQueue = new ArrayList<>();
|
||||
mediaPeriodIdToInfo = new HashMap<>();
|
||||
period = new Period();
|
||||
timeline = Timeline.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WindowAndMediaPeriodId} of the media period in the front of the queue.
|
||||
* This is the playing media period unless the player hasn't started playing yet (in which case
|
||||
* it is the loading media period or null). While the player is seeking or preparing, this
|
||||
* method will always return null to reflect the uncertainty about the current playing period.
|
||||
* May also be null, if the timeline is empty or no media period is active yet.
|
||||
* Returns the {@link MediaPeriodInfo} of the media period in the front of the queue. This is
|
||||
* the playing media period unless the player hasn't started playing yet (in which case it is
|
||||
* the loading media period or null). While the player is seeking or preparing, this method will
|
||||
* always return null to reflect the uncertainty about the current playing period. May also be
|
||||
* null, if the timeline is empty or no media period is active yet.
|
||||
*/
|
||||
public @Nullable WindowAndMediaPeriodId getPlayingMediaPeriod() {
|
||||
return activeMediaPeriods.isEmpty() || timeline.isEmpty() || isSeeking
|
||||
public @Nullable MediaPeriodInfo getPlayingMediaPeriod() {
|
||||
return mediaPeriodInfoQueue.isEmpty() || timeline.isEmpty() || isSeeking
|
||||
? null
|
||||
: activeMediaPeriods.get(0);
|
||||
: mediaPeriodInfoQueue.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WindowAndMediaPeriodId} of the currently playing media period. This is the
|
||||
* Returns the {@link MediaPeriodInfo} of the currently playing media period. This is the
|
||||
* publicly reported period which should always match {@link Player#getCurrentPeriodIndex()}
|
||||
* unless the player is currently seeking or being prepared in which case the previous period is
|
||||
* reported until the seek or preparation is processed. May be null, if no media period is
|
||||
* active yet.
|
||||
*/
|
||||
public @Nullable WindowAndMediaPeriodId getLastReportedPlayingMediaPeriod() {
|
||||
public @Nullable MediaPeriodInfo getLastReportedPlayingMediaPeriod() {
|
||||
return lastReportedPlayingMediaPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WindowAndMediaPeriodId} of the media period currently being read by the
|
||||
* player. May be null, if the player is not reading a media period.
|
||||
* Returns the {@link MediaPeriodInfo} of the media period currently being read by the player.
|
||||
* May be null, if the player is not reading a media period.
|
||||
*/
|
||||
public @Nullable WindowAndMediaPeriodId getReadingMediaPeriod() {
|
||||
public @Nullable MediaPeriodInfo getReadingMediaPeriod() {
|
||||
return readingMediaPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link MediaPeriodId} of the media period at the end of the queue which is
|
||||
* Returns the {@link MediaPeriodInfo} of the media period at the end of the queue which is
|
||||
* currently loading or will be the next one loading. May be null, if no media period is active
|
||||
* yet.
|
||||
*/
|
||||
public @Nullable WindowAndMediaPeriodId getLoadingMediaPeriod() {
|
||||
return activeMediaPeriods.isEmpty()
|
||||
public @Nullable MediaPeriodInfo getLoadingMediaPeriod() {
|
||||
return mediaPeriodInfoQueue.isEmpty()
|
||||
? null
|
||||
: activeMediaPeriods.get(activeMediaPeriods.size() - 1);
|
||||
: mediaPeriodInfoQueue.get(mediaPeriodInfoQueue.size() - 1);
|
||||
}
|
||||
|
||||
/** Returns the {@link MediaPeriodInfo} for the given {@link MediaPeriodId}. */
|
||||
public @Nullable MediaPeriodInfo getMediaPeriodInfo(MediaPeriodId mediaPeriodId) {
|
||||
return mediaPeriodIdToInfo.get(mediaPeriodId);
|
||||
}
|
||||
|
||||
/** Returns whether the player is currently seeking. */
|
||||
@ -699,23 +733,21 @@ public class AnalyticsCollector
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find an existing media period id from the specified window index. Only returns a
|
||||
* non-null media period id if there is a unique, unambiguous match.
|
||||
* Tries to find an existing media period info from the specified window index. Only returns a
|
||||
* non-null media period info if there is a unique, unambiguous match.
|
||||
*/
|
||||
public @Nullable MediaPeriodId tryResolveWindowIndex(int windowIndex) {
|
||||
MediaPeriodId match = null;
|
||||
if (timeline != null) {
|
||||
for (int i = 0; i < activeMediaPeriods.size(); i++) {
|
||||
WindowAndMediaPeriodId mediaPeriod = activeMediaPeriods.get(i);
|
||||
int periodIndex = timeline.getIndexOfPeriod(mediaPeriod.mediaPeriodId.periodUid);
|
||||
if (periodIndex != C.INDEX_UNSET
|
||||
&& timeline.getPeriod(periodIndex, period).windowIndex == windowIndex) {
|
||||
if (match != null) {
|
||||
// Ambiguous match.
|
||||
return null;
|
||||
}
|
||||
match = mediaPeriod.mediaPeriodId;
|
||||
public @Nullable MediaPeriodInfo tryResolveWindowIndex(int windowIndex) {
|
||||
MediaPeriodInfo match = null;
|
||||
for (int i = 0; i < mediaPeriodInfoQueue.size(); i++) {
|
||||
MediaPeriodInfo info = mediaPeriodInfoQueue.get(i);
|
||||
int periodIndex = timeline.getIndexOfPeriod(info.mediaPeriodId.periodUid);
|
||||
if (periodIndex != C.INDEX_UNSET
|
||||
&& timeline.getPeriod(periodIndex, period).windowIndex == windowIndex) {
|
||||
if (match != null) {
|
||||
// Ambiguous match.
|
||||
return null;
|
||||
}
|
||||
match = info;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
@ -728,12 +760,14 @@ public class AnalyticsCollector
|
||||
|
||||
/** Updates the queue with a reported timeline change. */
|
||||
public void onTimelineChanged(Timeline timeline) {
|
||||
for (int i = 0; i < activeMediaPeriods.size(); i++) {
|
||||
activeMediaPeriods.set(
|
||||
i, updateWindowIndexToNewTimeline(activeMediaPeriods.get(i), timeline));
|
||||
for (int i = 0; i < mediaPeriodInfoQueue.size(); i++) {
|
||||
MediaPeriodInfo newMediaPeriodInfo =
|
||||
updateMediaPeriodInfoToNewTimeline(mediaPeriodInfoQueue.get(i), timeline);
|
||||
mediaPeriodInfoQueue.set(i, newMediaPeriodInfo);
|
||||
mediaPeriodIdToInfo.put(newMediaPeriodInfo.mediaPeriodId, newMediaPeriodInfo);
|
||||
}
|
||||
if (readingMediaPeriod != null) {
|
||||
readingMediaPeriod = updateWindowIndexToNewTimeline(readingMediaPeriod, timeline);
|
||||
readingMediaPeriod = updateMediaPeriodInfoToNewTimeline(readingMediaPeriod, timeline);
|
||||
}
|
||||
this.timeline = timeline;
|
||||
updateLastReportedPlayingMediaPeriod();
|
||||
@ -752,71 +786,76 @@ public class AnalyticsCollector
|
||||
|
||||
/** Updates the queue with a newly created media period. */
|
||||
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
activeMediaPeriods.add(new WindowAndMediaPeriodId(windowIndex, mediaPeriodId));
|
||||
if (activeMediaPeriods.size() == 1 && !timeline.isEmpty()) {
|
||||
boolean isInTimeline = timeline.getIndexOfPeriod(mediaPeriodId.periodUid) != C.INDEX_UNSET;
|
||||
MediaPeriodInfo mediaPeriodInfo =
|
||||
new MediaPeriodInfo(mediaPeriodId, isInTimeline ? timeline : Timeline.EMPTY, windowIndex);
|
||||
mediaPeriodInfoQueue.add(mediaPeriodInfo);
|
||||
mediaPeriodIdToInfo.put(mediaPeriodId, mediaPeriodInfo);
|
||||
if (mediaPeriodInfoQueue.size() == 1 && !timeline.isEmpty()) {
|
||||
updateLastReportedPlayingMediaPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the queue with a released media period. */
|
||||
public void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
WindowAndMediaPeriodId mediaPeriod = new WindowAndMediaPeriodId(windowIndex, mediaPeriodId);
|
||||
activeMediaPeriods.remove(mediaPeriod);
|
||||
if (mediaPeriod.equals(readingMediaPeriod)) {
|
||||
readingMediaPeriod = activeMediaPeriods.isEmpty() ? null : activeMediaPeriods.get(0);
|
||||
/**
|
||||
* Updates the queue with a released media period. Returns whether the media period was still in
|
||||
* the queue.
|
||||
*/
|
||||
public boolean onMediaPeriodReleased(MediaPeriodId mediaPeriodId) {
|
||||
MediaPeriodInfo mediaPeriodInfo = mediaPeriodIdToInfo.remove(mediaPeriodId);
|
||||
if (mediaPeriodInfo == null) {
|
||||
// The media period has already been removed from the queue in resetForNewMediaSource().
|
||||
return false;
|
||||
}
|
||||
mediaPeriodInfoQueue.remove(mediaPeriodInfo);
|
||||
if (readingMediaPeriod != null && mediaPeriodId.equals(readingMediaPeriod.mediaPeriodId)) {
|
||||
readingMediaPeriod = mediaPeriodInfoQueue.isEmpty() ? null : mediaPeriodInfoQueue.get(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Update the queue with a change in the reading media period. */
|
||||
public void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
readingMediaPeriod = new WindowAndMediaPeriodId(windowIndex, mediaPeriodId);
|
||||
public void onReadingStarted(MediaPeriodId mediaPeriodId) {
|
||||
readingMediaPeriod = mediaPeriodIdToInfo.get(mediaPeriodId);
|
||||
}
|
||||
|
||||
private void updateLastReportedPlayingMediaPeriod() {
|
||||
if (!activeMediaPeriods.isEmpty()) {
|
||||
lastReportedPlayingMediaPeriod = activeMediaPeriods.get(0);
|
||||
if (!mediaPeriodInfoQueue.isEmpty()) {
|
||||
lastReportedPlayingMediaPeriod = mediaPeriodInfoQueue.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
private WindowAndMediaPeriodId updateWindowIndexToNewTimeline(
|
||||
WindowAndMediaPeriodId mediaPeriod, Timeline newTimeline) {
|
||||
if (newTimeline.isEmpty() || timeline.isEmpty()) {
|
||||
return mediaPeriod;
|
||||
}
|
||||
int newPeriodIndex = newTimeline.getIndexOfPeriod(mediaPeriod.mediaPeriodId.periodUid);
|
||||
private MediaPeriodInfo updateMediaPeriodInfoToNewTimeline(
|
||||
MediaPeriodInfo info, Timeline newTimeline) {
|
||||
int newPeriodIndex = newTimeline.getIndexOfPeriod(info.mediaPeriodId.periodUid);
|
||||
if (newPeriodIndex == C.INDEX_UNSET) {
|
||||
return mediaPeriod;
|
||||
// Media period is not yet or no longer available in the new timeline. Keep it as it is.
|
||||
return info;
|
||||
}
|
||||
int newWindowIndex = newTimeline.getPeriod(newPeriodIndex, period).windowIndex;
|
||||
return new WindowAndMediaPeriodId(newWindowIndex, mediaPeriod.mediaPeriodId);
|
||||
return new MediaPeriodInfo(info.mediaPeriodId, newTimeline, newWindowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class WindowAndMediaPeriodId {
|
||||
/** Information about a media period and its associated timeline. */
|
||||
private static final class MediaPeriodInfo {
|
||||
|
||||
public final int windowIndex;
|
||||
/** The {@link MediaPeriodId} of the media period. */
|
||||
public final MediaPeriodId mediaPeriodId;
|
||||
/**
|
||||
* The {@link Timeline} in which the media period can be found. Or {@link Timeline#EMPTY} if the
|
||||
* media period is not part of a known timeline yet.
|
||||
*/
|
||||
public final Timeline timeline;
|
||||
/**
|
||||
* The window index of the media period in the timeline. If the timeline is empty, this is the
|
||||
* prospective window index.
|
||||
*/
|
||||
public final int windowIndex;
|
||||
|
||||
public WindowAndMediaPeriodId(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
this.windowIndex = windowIndex;
|
||||
public MediaPeriodInfo(MediaPeriodId mediaPeriodId, Timeline timeline, int windowIndex) {
|
||||
this.mediaPeriodId = mediaPeriodId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
WindowAndMediaPeriodId that = (WindowAndMediaPeriodId) other;
|
||||
return windowIndex == that.windowIndex && mediaPeriodId.equals(that.mediaPeriodId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * windowIndex + mediaPeriodId.hashCode();
|
||||
this.timeline = timeline;
|
||||
this.windowIndex = windowIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -619,7 +619,7 @@ public final class AnalyticsCollectorTest {
|
||||
.containsExactly(window0Period1Seq0, window1Period0Seq1);
|
||||
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED))
|
||||
.containsExactly(window0Period1Seq0, window1Period0Seq1);
|
||||
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(period0Seq1);
|
||||
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(window1Period0Seq1);
|
||||
assertThat(listener.getEvents(EVENT_READING_STARTED))
|
||||
.containsExactly(window0Period1Seq0, window1Period0Seq1);
|
||||
assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
|
||||
|
Loading…
x
Reference in New Issue
Block a user