Commit 20820800 by bachinger Committed by Oliver Woodman

Prevent parallel timeline access in MaskingMediaSource

The list of MediaSourceHolder in ExoPlayerImpl is only maintained to be able to create a PlaylistTimeline for masking. By keeping only the id and a snapshot of the timeline of the MediaSourceHolder in ExoPlayerImpl, parallel access is prevented and we still have sufficient information to create the masking timeline.

PiperOrigin-RevId: 319003837
parent a3bbcf33
...@@ -64,6 +64,7 @@ import java.lang.reflect.Method; ...@@ -64,6 +64,7 @@ import java.lang.reflect.Method;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
...@@ -2186,6 +2187,24 @@ public final class Util { ...@@ -2186,6 +2187,24 @@ public final class Util {
: SystemClock.elapsedRealtime() + elapsedRealtimeEpochOffsetMs; : SystemClock.elapsedRealtime() + elapsedRealtimeEpochOffsetMs;
} }
/**
* Moves the elements starting at {@code fromIndex} to {@code newFromIndex}.
*
* @param items The list of which to move elements.
* @param fromIndex The index at which the items to move start.
* @param toIndex The index up to which elements should be moved (exclusive).
* @param newFromIndex The new from index.
*/
public static <T extends Object> void moveItems(
List<T> items, int fromIndex, int toIndex, int newFromIndex) {
ArrayDeque<T> removedItems = new ArrayDeque<>();
int removedItemsLength = toIndex - fromIndex;
for (int i = removedItemsLength - 1; i >= 0; i--) {
removedItems.addFirst(items.remove(fromIndex + i));
}
items.addAll(Math.min(newFromIndex, items.size()), removedItems);
}
@Nullable @Nullable
private static String getSystemProperty(String name) { private static String getSystemProperty(String name) {
try { try {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Handler; import android.os.Handler;
...@@ -71,7 +72,7 @@ import java.util.concurrent.TimeoutException; ...@@ -71,7 +72,7 @@ import java.util.concurrent.TimeoutException;
private final CopyOnWriteArrayList<ListenerHolder> listeners; private final CopyOnWriteArrayList<ListenerHolder> listeners;
private final Timeline.Period period; private final Timeline.Period period;
private final ArrayDeque<Runnable> pendingListenerNotifications; private final ArrayDeque<Runnable> pendingListenerNotifications;
private final List<MediaSourceList.MediaSourceHolder> mediaSourceHolders; private final List<MediaSourceHolderSnapshot> mediaSourceHolderSnapshots;
private final boolean useLazyPreparation; private final boolean useLazyPreparation;
private final MediaSourceFactory mediaSourceFactory; private final MediaSourceFactory mediaSourceFactory;
...@@ -130,7 +131,7 @@ import java.util.concurrent.TimeoutException; ...@@ -130,7 +131,7 @@ import java.util.concurrent.TimeoutException;
Looper applicationLooper) { Looper applicationLooper) {
Log.i(TAG, "Init " + Integer.toHexString(System.identityHashCode(this)) + " [" Log.i(TAG, "Init " + Integer.toHexString(System.identityHashCode(this)) + " ["
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "]"); + ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "]");
Assertions.checkState(renderers.length > 0); checkState(renderers.length > 0);
this.renderers = checkNotNull(renderers); this.renderers = checkNotNull(renderers);
this.trackSelector = checkNotNull(trackSelector); this.trackSelector = checkNotNull(trackSelector);
this.mediaSourceFactory = mediaSourceFactory; this.mediaSourceFactory = mediaSourceFactory;
...@@ -139,7 +140,7 @@ import java.util.concurrent.TimeoutException; ...@@ -139,7 +140,7 @@ import java.util.concurrent.TimeoutException;
this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems; this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems;
repeatMode = Player.REPEAT_MODE_OFF; repeatMode = Player.REPEAT_MODE_OFF;
listeners = new CopyOnWriteArrayList<>(); listeners = new CopyOnWriteArrayList<>();
mediaSourceHolders = new ArrayList<>(); mediaSourceHolderSnapshots = new ArrayList<>();
shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0);
emptyTrackSelectorResult = emptyTrackSelectorResult =
new TrackSelectorResult( new TrackSelectorResult(
...@@ -387,7 +388,7 @@ import java.util.concurrent.TimeoutException; ...@@ -387,7 +388,7 @@ import java.util.concurrent.TimeoutException;
@Override @Override
public void addMediaItems(List<MediaItem> mediaItems) { public void addMediaItems(List<MediaItem> mediaItems) {
addMediaItems(/* index= */ mediaSourceHolders.size(), mediaItems); addMediaItems(/* index= */ mediaSourceHolderSnapshots.size(), mediaItems);
} }
@Override @Override
...@@ -407,7 +408,7 @@ import java.util.concurrent.TimeoutException; ...@@ -407,7 +408,7 @@ import java.util.concurrent.TimeoutException;
@Override @Override
public void addMediaSources(List<MediaSource> mediaSources) { public void addMediaSources(List<MediaSource> mediaSources) {
addMediaSources(/* index= */ mediaSourceHolders.size(), mediaSources); addMediaSources(/* index= */ mediaSourceHolderSnapshots.size(), mediaSources);
} }
@Override @Override
...@@ -442,14 +443,15 @@ import java.util.concurrent.TimeoutException; ...@@ -442,14 +443,15 @@ import java.util.concurrent.TimeoutException;
Assertions.checkArgument( Assertions.checkArgument(
fromIndex >= 0 fromIndex >= 0
&& fromIndex <= toIndex && fromIndex <= toIndex
&& toIndex <= mediaSourceHolders.size() && toIndex <= mediaSourceHolderSnapshots.size()
&& newFromIndex >= 0); && newFromIndex >= 0);
int currentWindowIndex = getCurrentWindowIndex(); int currentWindowIndex = getCurrentWindowIndex();
long currentPositionMs = getCurrentPosition(); long currentPositionMs = getCurrentPosition();
Timeline oldTimeline = getCurrentTimeline(); Timeline oldTimeline = getCurrentTimeline();
pendingOperationAcks++; pendingOperationAcks++;
newFromIndex = Math.min(newFromIndex, mediaSourceHolders.size() - (toIndex - fromIndex)); newFromIndex =
MediaSourceList.moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex); Math.min(newFromIndex, mediaSourceHolderSnapshots.size() - (toIndex - fromIndex));
Util.moveItems(mediaSourceHolderSnapshots, fromIndex, toIndex, newFromIndex);
PlaybackInfo playbackInfo = PlaybackInfo playbackInfo =
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline); maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder); internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder);
...@@ -464,10 +466,10 @@ import java.util.concurrent.TimeoutException; ...@@ -464,10 +466,10 @@ import java.util.concurrent.TimeoutException;
@Override @Override
public void clearMediaItems() { public void clearMediaItems() {
if (mediaSourceHolders.isEmpty()) { if (mediaSourceHolderSnapshots.isEmpty()) {
return; return;
} }
removeMediaItemsInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolders.size()); removeMediaItemsInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolderSnapshots.size());
} }
@Override @Override
...@@ -624,7 +626,7 @@ import java.util.concurrent.TimeoutException; ...@@ -624,7 +626,7 @@ import java.util.concurrent.TimeoutException;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void setPlaybackSpeed(float playbackSpeed) { public void setPlaybackSpeed(float playbackSpeed) {
Assertions.checkState(playbackSpeed > 0); checkState(playbackSpeed > 0);
if (this.playbackSpeed == playbackSpeed) { if (this.playbackSpeed == playbackSpeed) {
return; return;
} }
...@@ -918,11 +920,18 @@ import java.util.concurrent.TimeoutException; ...@@ -918,11 +920,18 @@ import java.util.concurrent.TimeoutException;
pendingPlayWhenReadyChangeReason = playbackInfoUpdate.playWhenReadyChangeReason; pendingPlayWhenReadyChangeReason = playbackInfoUpdate.playWhenReadyChangeReason;
} }
if (pendingOperationAcks == 0) { if (pendingOperationAcks == 0) {
if (!this.playbackInfo.timeline.isEmpty() Timeline newTimeline = playbackInfoUpdate.playbackInfo.timeline;
&& playbackInfoUpdate.playbackInfo.timeline.isEmpty()) { if (!this.playbackInfo.timeline.isEmpty() && newTimeline.isEmpty()) {
// Update the masking variables, which are used when the timeline becomes empty. // Update the masking variables, which are used when the timeline becomes empty.
resetMaskingPosition(); resetMaskingPosition();
} }
if (!newTimeline.isEmpty()) {
List<Timeline> timelines = ((PlaylistTimeline) newTimeline).getChildTimelines();
checkState(timelines.size() == mediaSourceHolderSnapshots.size());
for (int i = 0; i < timelines.size(); i++) {
mediaSourceHolderSnapshots.get(i).timeline = timelines.get(i);
}
}
boolean positionDiscontinuity = hasPendingDiscontinuity; boolean positionDiscontinuity = hasPendingDiscontinuity;
hasPendingDiscontinuity = false; hasPendingDiscontinuity = false;
updatePlaybackInfo( updatePlaybackInfo(
...@@ -940,7 +949,7 @@ import java.util.concurrent.TimeoutException; ...@@ -940,7 +949,7 @@ import java.util.concurrent.TimeoutException;
if (clearPlaylist) { if (clearPlaylist) {
// Reset list of media source holders which are used for creating the masking timeline. // Reset list of media source holders which are used for creating the masking timeline.
removeMediaSourceHolders( removeMediaSourceHolders(
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size()); /* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolderSnapshots.size());
resetMaskingPosition(); resetMaskingPosition();
} else { } else {
maskWithCurrentPosition(); maskWithCurrentPosition();
...@@ -1004,9 +1013,9 @@ import java.util.concurrent.TimeoutException; ...@@ -1004,9 +1013,9 @@ import java.util.concurrent.TimeoutException;
int currentWindowIndex = getCurrentWindowIndexInternal(); int currentWindowIndex = getCurrentWindowIndexInternal();
long currentPositionMs = getCurrentPosition(); long currentPositionMs = getCurrentPosition();
pendingOperationAcks++; pendingOperationAcks++;
if (!mediaSourceHolders.isEmpty()) { if (!mediaSourceHolderSnapshots.isEmpty()) {
removeMediaSourceHolders( removeMediaSourceHolders(
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size()); /* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolderSnapshots.size());
} }
List<MediaSourceList.MediaSourceHolder> holders = List<MediaSourceList.MediaSourceHolder> holders =
addMediaSourceHolders(/* index= */ 0, mediaSources); addMediaSourceHolders(/* index= */ 0, mediaSources);
...@@ -1055,7 +1064,8 @@ import java.util.concurrent.TimeoutException; ...@@ -1055,7 +1064,8 @@ import java.util.concurrent.TimeoutException;
MediaSourceList.MediaSourceHolder holder = MediaSourceList.MediaSourceHolder holder =
new MediaSourceList.MediaSourceHolder(mediaSources.get(i), useLazyPreparation); new MediaSourceList.MediaSourceHolder(mediaSources.get(i), useLazyPreparation);
holders.add(holder); holders.add(holder);
mediaSourceHolders.add(i + index, holder); mediaSourceHolderSnapshots.add(
i + index, new MediaSourceHolderSnapshot(holder.uid, holder.mediaSource.getTimeline()));
} }
shuffleOrder = shuffleOrder =
shuffleOrder.cloneAndInsert( shuffleOrder.cloneAndInsert(
...@@ -1065,11 +1075,11 @@ import java.util.concurrent.TimeoutException; ...@@ -1065,11 +1075,11 @@ import java.util.concurrent.TimeoutException;
private void removeMediaItemsInternal(int fromIndex, int toIndex) { private void removeMediaItemsInternal(int fromIndex, int toIndex) {
Assertions.checkArgument( Assertions.checkArgument(
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolders.size()); fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolderSnapshots.size());
int currentWindowIndex = getCurrentWindowIndex(); int currentWindowIndex = getCurrentWindowIndex();
long currentPositionMs = getCurrentPosition(); long currentPositionMs = getCurrentPosition();
Timeline oldTimeline = getCurrentTimeline(); Timeline oldTimeline = getCurrentTimeline();
int currentMediaSourceCount = mediaSourceHolders.size(); int currentMediaSourceCount = mediaSourceHolderSnapshots.size();
pendingOperationAcks++; pendingOperationAcks++;
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex); removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex);
PlaybackInfo playbackInfo = PlaybackInfo playbackInfo =
...@@ -1094,17 +1104,14 @@ import java.util.concurrent.TimeoutException; ...@@ -1094,17 +1104,14 @@ import java.util.concurrent.TimeoutException;
/* seekProcessed= */ false); /* seekProcessed= */ false);
} }
private List<MediaSourceList.MediaSourceHolder> removeMediaSourceHolders( private void removeMediaSourceHolders(int fromIndex, int toIndexExclusive) {
int fromIndex, int toIndexExclusive) {
List<MediaSourceList.MediaSourceHolder> removed = new ArrayList<>();
for (int i = toIndexExclusive - 1; i >= fromIndex; i--) { for (int i = toIndexExclusive - 1; i >= fromIndex; i--) {
removed.add(mediaSourceHolders.remove(i)); mediaSourceHolderSnapshots.remove(i);
} }
shuffleOrder = shuffleOrder.cloneAndRemove(fromIndex, toIndexExclusive); shuffleOrder = shuffleOrder.cloneAndRemove(fromIndex, toIndexExclusive);
if (mediaSourceHolders.isEmpty()) { if (mediaSourceHolderSnapshots.isEmpty()) {
hasAdsMediaSource = false; hasAdsMediaSource = false;
} }
return removed;
} }
/** /**
...@@ -1123,7 +1130,7 @@ import java.util.concurrent.TimeoutException; ...@@ -1123,7 +1130,7 @@ import java.util.concurrent.TimeoutException;
throw new IllegalStateException(); throw new IllegalStateException();
} }
int sizeAfterModification = int sizeAfterModification =
mediaSources.size() + (mediaSourceReplacement ? 0 : mediaSourceHolders.size()); mediaSources.size() + (mediaSourceReplacement ? 0 : mediaSourceHolderSnapshots.size());
for (int i = 0; i < mediaSources.size(); i++) { for (int i = 0; i < mediaSources.size(); i++) {
MediaSource mediaSource = checkNotNull(mediaSources.get(i)); MediaSource mediaSource = checkNotNull(mediaSources.get(i));
if (mediaSource instanceof AdsMediaSource) { if (mediaSource instanceof AdsMediaSource) {
...@@ -1139,9 +1146,9 @@ import java.util.concurrent.TimeoutException; ...@@ -1139,9 +1146,9 @@ import java.util.concurrent.TimeoutException;
private PlaybackInfo maskTimeline() { private PlaybackInfo maskTimeline() {
return playbackInfo.copyWithTimeline( return playbackInfo.copyWithTimeline(
mediaSourceHolders.isEmpty() mediaSourceHolderSnapshots.isEmpty()
? Timeline.EMPTY ? Timeline.EMPTY
: new MediaSourceList.PlaylistTimeline(mediaSourceHolders, shuffleOrder)); : new PlaylistTimeline(mediaSourceHolderSnapshots, shuffleOrder));
} }
private PlaybackInfo maskTimelineAndWindowIndex( private PlaybackInfo maskTimelineAndWindowIndex(
...@@ -1395,4 +1402,26 @@ import java.util.concurrent.TimeoutException; ...@@ -1395,4 +1402,26 @@ import java.util.concurrent.TimeoutException;
listenerHolder.invoke(listenerInvocation); listenerHolder.invoke(listenerInvocation);
} }
} }
private static final class MediaSourceHolderSnapshot implements MediaSourceInfoHolder {
private final Object uid;
private Timeline timeline;
public MediaSourceHolderSnapshot(Object uid, Timeline timeline) {
this.uid = uid;
this.timeline = timeline;
}
@Override
public Object getUid() {
return uid;
}
@Override
public Timeline getTimeline() {
return timeline;
}
}
} }
...@@ -596,7 +596,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -596,7 +596,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (mediaSourceListUpdateMessage.windowIndex != C.INDEX_UNSET) { if (mediaSourceListUpdateMessage.windowIndex != C.INDEX_UNSET) {
pendingInitialSeekPosition = pendingInitialSeekPosition =
new SeekPosition( new SeekPosition(
new MediaSourceList.PlaylistTimeline( new PlaylistTimeline(
mediaSourceListUpdateMessage.mediaSourceHolders, mediaSourceListUpdateMessage.mediaSourceHolders,
mediaSourceListUpdateMessage.shuffleOrder), mediaSourceListUpdateMessage.shuffleOrder),
mediaSourceListUpdateMessage.windowIndex, mediaSourceListUpdateMessage.windowIndex,
......
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
import com.google.android.exoplayer2.source.MediaSource;
/** A holder of information about a {@link MediaSource}. */
/* package */ interface MediaSourceInfoHolder {
/** Returns the uid of the {@link MediaSourceList.MediaSourceHolder}. */
Object getUid();
/** Returns the timeline. */
Timeline getTimeline();
}
...@@ -35,8 +35,6 @@ import com.google.android.exoplayer2.util.Log; ...@@ -35,8 +35,6 @@ import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
...@@ -234,7 +232,7 @@ import java.util.Set; ...@@ -234,7 +232,7 @@ import java.util.Set;
int newEndIndex = newFromIndex + (toIndex - fromIndex) - 1; int newEndIndex = newFromIndex + (toIndex - fromIndex) - 1;
int endIndex = Math.max(newEndIndex, toIndex - 1); int endIndex = Math.max(newEndIndex, toIndex - 1);
int windowOffset = mediaSourceHolders.get(startIndex).firstWindowIndexInChild; int windowOffset = mediaSourceHolders.get(startIndex).firstWindowIndexInChild;
moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex); Util.moveItems(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
for (int i = startIndex; i <= endIndex; i++) { for (int i = startIndex; i <= endIndex; i++) {
MediaSourceHolder holder = mediaSourceHolders.get(i); MediaSourceHolder holder = mediaSourceHolders.get(i);
holder.firstWindowIndexInChild = windowOffset; holder.firstWindowIndexInChild = windowOffset;
...@@ -472,18 +470,8 @@ import java.util.Set; ...@@ -472,18 +470,8 @@ import java.util.Set;
return PlaylistTimeline.getConcatenatedUid(holder.uid, childPeriodUid); return PlaylistTimeline.getConcatenatedUid(holder.uid, childPeriodUid);
} }
/* package */ static void moveMediaSourceHolders(
List<MediaSourceHolder> mediaSourceHolders, int fromIndex, int toIndex, int newFromIndex) {
MediaSourceHolder[] removedItems = new MediaSourceHolder[toIndex - fromIndex];
for (int i = removedItems.length - 1; i >= 0; i--) {
removedItems[i] = mediaSourceHolders.remove(fromIndex + i);
}
mediaSourceHolders.addAll(
Math.min(newFromIndex, mediaSourceHolders.size()), Arrays.asList(removedItems));
}
/** Data class to hold playlist media sources together with meta data needed to process them. */ /** Data class to hold playlist media sources together with meta data needed to process them. */
/* package */ static final class MediaSourceHolder { /* package */ static final class MediaSourceHolder implements MediaSourceInfoHolder {
public final MaskingMediaSource mediaSource; public final MaskingMediaSource mediaSource;
public final Object uid; public final Object uid;
...@@ -503,88 +491,15 @@ import java.util.Set; ...@@ -503,88 +491,15 @@ import java.util.Set;
this.isRemoved = false; this.isRemoved = false;
this.activeMediaPeriodIds.clear(); this.activeMediaPeriodIds.clear();
} }
}
/** Timeline exposing concatenated timelines of playlist media sources. */
/* package */ static final class PlaylistTimeline extends AbstractConcatenatedTimeline {
private final int windowCount;
private final int periodCount;
private final int[] firstPeriodInChildIndices;
private final int[] firstWindowInChildIndices;
private final Timeline[] timelines;
private final Object[] uids;
private final HashMap<Object, Integer> childIndexByUid;
public PlaylistTimeline(
Collection<MediaSourceHolder> mediaSourceHolders, ShuffleOrder shuffleOrder) {
super(/* isAtomic= */ false, shuffleOrder);
int childCount = mediaSourceHolders.size();
firstPeriodInChildIndices = new int[childCount];
firstWindowInChildIndices = new int[childCount];
timelines = new Timeline[childCount];
uids = new Object[childCount];
childIndexByUid = new HashMap<>();
int index = 0;
int windowCount = 0;
int periodCount = 0;
for (MediaSourceHolder mediaSourceHolder : mediaSourceHolders) {
timelines[index] = mediaSourceHolder.mediaSource.getTimeline();
firstWindowInChildIndices[index] = windowCount;
firstPeriodInChildIndices[index] = periodCount;
windowCount += timelines[index].getWindowCount();
periodCount += timelines[index].getPeriodCount();
uids[index] = mediaSourceHolder.uid;
childIndexByUid.put(uids[index], index++);
}
this.windowCount = windowCount;
this.periodCount = periodCount;
}
@Override
protected int getChildIndexByPeriodIndex(int periodIndex) {
return Util.binarySearchFloor(firstPeriodInChildIndices, periodIndex + 1, false, false);
}
@Override
protected int getChildIndexByWindowIndex(int windowIndex) {
return Util.binarySearchFloor(firstWindowInChildIndices, windowIndex + 1, false, false);
}
@Override
protected int getChildIndexByChildUid(Object childUid) {
Integer index = childIndexByUid.get(childUid);
return index == null ? C.INDEX_UNSET : index;
}
@Override
protected Timeline getTimelineByChildIndex(int childIndex) {
return timelines[childIndex];
}
@Override
protected int getFirstPeriodIndexByChildIndex(int childIndex) {
return firstPeriodInChildIndices[childIndex];
}
@Override
protected int getFirstWindowIndexByChildIndex(int childIndex) {
return firstWindowInChildIndices[childIndex];
}
@Override
protected Object getChildUidByChildIndex(int childIndex) {
return uids[childIndex];
}
@Override @Override
public int getWindowCount() { public Object getUid() {
return windowCount; return uid;
} }
@Override @Override
public int getPeriodCount() { public Timeline getTimeline() {
return periodCount; return mediaSource.getTimeline();
} }
} }
......
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
import com.google.android.exoplayer2.source.ShuffleOrder;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
/** Timeline exposing concatenated timelines of playlist media sources. */
/* package */ final class PlaylistTimeline extends AbstractConcatenatedTimeline {
private final int windowCount;
private final int periodCount;
private final int[] firstPeriodInChildIndices;
private final int[] firstWindowInChildIndices;
private final Timeline[] timelines;
private final Object[] uids;
private final HashMap<Object, Integer> childIndexByUid;
/** Creates an instance. */
public PlaylistTimeline(
Collection<? extends MediaSourceInfoHolder> mediaSourceInfoHolders,
ShuffleOrder shuffleOrder) {
super(/* isAtomic= */ false, shuffleOrder);
int childCount = mediaSourceInfoHolders.size();
firstPeriodInChildIndices = new int[childCount];
firstWindowInChildIndices = new int[childCount];
timelines = new Timeline[childCount];
uids = new Object[childCount];
childIndexByUid = new HashMap<>();
int index = 0;
int windowCount = 0;
int periodCount = 0;
for (MediaSourceInfoHolder mediaSourceInfoHolder : mediaSourceInfoHolders) {
timelines[index] = mediaSourceInfoHolder.getTimeline();
firstWindowInChildIndices[index] = windowCount;
firstPeriodInChildIndices[index] = periodCount;
windowCount += timelines[index].getWindowCount();
periodCount += timelines[index].getPeriodCount();
uids[index] = mediaSourceInfoHolder.getUid();
childIndexByUid.put(uids[index], index++);
}
this.windowCount = windowCount;
this.periodCount = periodCount;
}
/** Returns the child timelines. */
/* package */ List<Timeline> getChildTimelines() {
return Arrays.asList(timelines);
}
@Override
protected int getChildIndexByPeriodIndex(int periodIndex) {
return Util.binarySearchFloor(firstPeriodInChildIndices, periodIndex + 1, false, false);
}
@Override
protected int getChildIndexByWindowIndex(int windowIndex) {
return Util.binarySearchFloor(firstWindowInChildIndices, windowIndex + 1, false, false);
}
@Override
protected int getChildIndexByChildUid(Object childUid) {
Integer index = childIndexByUid.get(childUid);
return index == null ? C.INDEX_UNSET : index;
}
@Override
protected Timeline getTimelineByChildIndex(int childIndex) {
return timelines[childIndex];
}
@Override
protected int getFirstPeriodIndexByChildIndex(int childIndex) {
return firstPeriodInChildIndices[childIndex];
}
@Override
protected int getFirstWindowIndexByChildIndex(int childIndex) {
return firstWindowInChildIndices[childIndex];
}
@Override
protected Object getChildUidByChildIndex(int childIndex) {
return uids[childIndex];
}
@Override
public int getWindowCount() {
return windowCount;
}
@Override
public int getPeriodCount() {
return periodCount;
}
}
...@@ -72,7 +72,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> { ...@@ -72,7 +72,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
} }
/** Returns the {@link Timeline}. */ /** Returns the {@link Timeline}. */
public synchronized Timeline getTimeline() { public Timeline getTimeline() {
return timeline; return timeline;
} }
...@@ -150,7 +150,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> { ...@@ -150,7 +150,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
} }
@Override @Override
protected synchronized void onChildSourceInfoRefreshed( protected void onChildSourceInfoRefreshed(
Void id, MediaSource mediaSource, Timeline newTimeline) { Void id, MediaSource mediaSource, Timeline newTimeline) {
@Nullable MediaPeriodId idForMaskingPeriodPreparation = null; @Nullable MediaPeriodId idForMaskingPeriodPreparation = null;
if (isPrepared) { if (isPrepared) {
......
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