mirror of
https://github.com/samsonjs/media.git
synced 2026-04-06 11:25:46 +00:00
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
This commit is contained in:
parent
a3bbcf3395
commit
20820800f3
7 changed files with 227 additions and 123 deletions
|
|
@ -64,6 +64,7 @@ import java.lang.reflect.Method;
|
|||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
|
|
@ -2186,6 +2187,24 @@ public final class Util {
|
|||
: 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
|
||||
private static String getSystemProperty(String name) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Handler;
|
||||
|
|
@ -71,7 +72,7 @@ import java.util.concurrent.TimeoutException;
|
|||
private final CopyOnWriteArrayList<ListenerHolder> listeners;
|
||||
private final Timeline.Period period;
|
||||
private final ArrayDeque<Runnable> pendingListenerNotifications;
|
||||
private final List<MediaSourceList.MediaSourceHolder> mediaSourceHolders;
|
||||
private final List<MediaSourceHolderSnapshot> mediaSourceHolderSnapshots;
|
||||
private final boolean useLazyPreparation;
|
||||
private final MediaSourceFactory mediaSourceFactory;
|
||||
|
||||
|
|
@ -130,7 +131,7 @@ import java.util.concurrent.TimeoutException;
|
|||
Looper applicationLooper) {
|
||||
Log.i(TAG, "Init " + Integer.toHexString(System.identityHashCode(this)) + " ["
|
||||
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "]");
|
||||
Assertions.checkState(renderers.length > 0);
|
||||
checkState(renderers.length > 0);
|
||||
this.renderers = checkNotNull(renderers);
|
||||
this.trackSelector = checkNotNull(trackSelector);
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
|
|
@ -139,7 +140,7 @@ import java.util.concurrent.TimeoutException;
|
|||
this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems;
|
||||
repeatMode = Player.REPEAT_MODE_OFF;
|
||||
listeners = new CopyOnWriteArrayList<>();
|
||||
mediaSourceHolders = new ArrayList<>();
|
||||
mediaSourceHolderSnapshots = new ArrayList<>();
|
||||
shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0);
|
||||
emptyTrackSelectorResult =
|
||||
new TrackSelectorResult(
|
||||
|
|
@ -387,7 +388,7 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
@Override
|
||||
public void addMediaItems(List<MediaItem> mediaItems) {
|
||||
addMediaItems(/* index= */ mediaSourceHolders.size(), mediaItems);
|
||||
addMediaItems(/* index= */ mediaSourceHolderSnapshots.size(), mediaItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -407,7 +408,7 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
@Override
|
||||
public void addMediaSources(List<MediaSource> mediaSources) {
|
||||
addMediaSources(/* index= */ mediaSourceHolders.size(), mediaSources);
|
||||
addMediaSources(/* index= */ mediaSourceHolderSnapshots.size(), mediaSources);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -442,14 +443,15 @@ import java.util.concurrent.TimeoutException;
|
|||
Assertions.checkArgument(
|
||||
fromIndex >= 0
|
||||
&& fromIndex <= toIndex
|
||||
&& toIndex <= mediaSourceHolders.size()
|
||||
&& toIndex <= mediaSourceHolderSnapshots.size()
|
||||
&& newFromIndex >= 0);
|
||||
int currentWindowIndex = getCurrentWindowIndex();
|
||||
long currentPositionMs = getCurrentPosition();
|
||||
Timeline oldTimeline = getCurrentTimeline();
|
||||
pendingOperationAcks++;
|
||||
newFromIndex = Math.min(newFromIndex, mediaSourceHolders.size() - (toIndex - fromIndex));
|
||||
MediaSourceList.moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
|
||||
newFromIndex =
|
||||
Math.min(newFromIndex, mediaSourceHolderSnapshots.size() - (toIndex - fromIndex));
|
||||
Util.moveItems(mediaSourceHolderSnapshots, fromIndex, toIndex, newFromIndex);
|
||||
PlaybackInfo playbackInfo =
|
||||
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
||||
internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder);
|
||||
|
|
@ -464,10 +466,10 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
@Override
|
||||
public void clearMediaItems() {
|
||||
if (mediaSourceHolders.isEmpty()) {
|
||||
if (mediaSourceHolderSnapshots.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
removeMediaItemsInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolders.size());
|
||||
removeMediaItemsInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolderSnapshots.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -624,7 +626,7 @@ import java.util.concurrent.TimeoutException;
|
|||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void setPlaybackSpeed(float playbackSpeed) {
|
||||
Assertions.checkState(playbackSpeed > 0);
|
||||
checkState(playbackSpeed > 0);
|
||||
if (this.playbackSpeed == playbackSpeed) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -918,11 +920,18 @@ import java.util.concurrent.TimeoutException;
|
|||
pendingPlayWhenReadyChangeReason = playbackInfoUpdate.playWhenReadyChangeReason;
|
||||
}
|
||||
if (pendingOperationAcks == 0) {
|
||||
if (!this.playbackInfo.timeline.isEmpty()
|
||||
&& playbackInfoUpdate.playbackInfo.timeline.isEmpty()) {
|
||||
Timeline newTimeline = playbackInfoUpdate.playbackInfo.timeline;
|
||||
if (!this.playbackInfo.timeline.isEmpty() && newTimeline.isEmpty()) {
|
||||
// Update the masking variables, which are used when the timeline becomes empty.
|
||||
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;
|
||||
hasPendingDiscontinuity = false;
|
||||
updatePlaybackInfo(
|
||||
|
|
@ -940,7 +949,7 @@ import java.util.concurrent.TimeoutException;
|
|||
if (clearPlaylist) {
|
||||
// Reset list of media source holders which are used for creating the masking timeline.
|
||||
removeMediaSourceHolders(
|
||||
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size());
|
||||
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolderSnapshots.size());
|
||||
resetMaskingPosition();
|
||||
} else {
|
||||
maskWithCurrentPosition();
|
||||
|
|
@ -1004,9 +1013,9 @@ import java.util.concurrent.TimeoutException;
|
|||
int currentWindowIndex = getCurrentWindowIndexInternal();
|
||||
long currentPositionMs = getCurrentPosition();
|
||||
pendingOperationAcks++;
|
||||
if (!mediaSourceHolders.isEmpty()) {
|
||||
if (!mediaSourceHolderSnapshots.isEmpty()) {
|
||||
removeMediaSourceHolders(
|
||||
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size());
|
||||
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolderSnapshots.size());
|
||||
}
|
||||
List<MediaSourceList.MediaSourceHolder> holders =
|
||||
addMediaSourceHolders(/* index= */ 0, mediaSources);
|
||||
|
|
@ -1055,7 +1064,8 @@ import java.util.concurrent.TimeoutException;
|
|||
MediaSourceList.MediaSourceHolder holder =
|
||||
new MediaSourceList.MediaSourceHolder(mediaSources.get(i), useLazyPreparation);
|
||||
holders.add(holder);
|
||||
mediaSourceHolders.add(i + index, holder);
|
||||
mediaSourceHolderSnapshots.add(
|
||||
i + index, new MediaSourceHolderSnapshot(holder.uid, holder.mediaSource.getTimeline()));
|
||||
}
|
||||
shuffleOrder =
|
||||
shuffleOrder.cloneAndInsert(
|
||||
|
|
@ -1065,11 +1075,11 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
private void removeMediaItemsInternal(int fromIndex, int toIndex) {
|
||||
Assertions.checkArgument(
|
||||
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolders.size());
|
||||
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolderSnapshots.size());
|
||||
int currentWindowIndex = getCurrentWindowIndex();
|
||||
long currentPositionMs = getCurrentPosition();
|
||||
Timeline oldTimeline = getCurrentTimeline();
|
||||
int currentMediaSourceCount = mediaSourceHolders.size();
|
||||
int currentMediaSourceCount = mediaSourceHolderSnapshots.size();
|
||||
pendingOperationAcks++;
|
||||
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex);
|
||||
PlaybackInfo playbackInfo =
|
||||
|
|
@ -1094,17 +1104,14 @@ import java.util.concurrent.TimeoutException;
|
|||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
private List<MediaSourceList.MediaSourceHolder> removeMediaSourceHolders(
|
||||
int fromIndex, int toIndexExclusive) {
|
||||
List<MediaSourceList.MediaSourceHolder> removed = new ArrayList<>();
|
||||
private void removeMediaSourceHolders(int fromIndex, int toIndexExclusive) {
|
||||
for (int i = toIndexExclusive - 1; i >= fromIndex; i--) {
|
||||
removed.add(mediaSourceHolders.remove(i));
|
||||
mediaSourceHolderSnapshots.remove(i);
|
||||
}
|
||||
shuffleOrder = shuffleOrder.cloneAndRemove(fromIndex, toIndexExclusive);
|
||||
if (mediaSourceHolders.isEmpty()) {
|
||||
if (mediaSourceHolderSnapshots.isEmpty()) {
|
||||
hasAdsMediaSource = false;
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1123,7 +1130,7 @@ import java.util.concurrent.TimeoutException;
|
|||
throw new IllegalStateException();
|
||||
}
|
||||
int sizeAfterModification =
|
||||
mediaSources.size() + (mediaSourceReplacement ? 0 : mediaSourceHolders.size());
|
||||
mediaSources.size() + (mediaSourceReplacement ? 0 : mediaSourceHolderSnapshots.size());
|
||||
for (int i = 0; i < mediaSources.size(); i++) {
|
||||
MediaSource mediaSource = checkNotNull(mediaSources.get(i));
|
||||
if (mediaSource instanceof AdsMediaSource) {
|
||||
|
|
@ -1139,9 +1146,9 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
private PlaybackInfo maskTimeline() {
|
||||
return playbackInfo.copyWithTimeline(
|
||||
mediaSourceHolders.isEmpty()
|
||||
mediaSourceHolderSnapshots.isEmpty()
|
||||
? Timeline.EMPTY
|
||||
: new MediaSourceList.PlaylistTimeline(mediaSourceHolders, shuffleOrder));
|
||||
: new PlaylistTimeline(mediaSourceHolderSnapshots, shuffleOrder));
|
||||
}
|
||||
|
||||
private PlaybackInfo maskTimelineAndWindowIndex(
|
||||
|
|
@ -1395,4 +1402,26 @@ import java.util.concurrent.TimeoutException;
|
|||
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;
|
|||
if (mediaSourceListUpdateMessage.windowIndex != C.INDEX_UNSET) {
|
||||
pendingInitialSeekPosition =
|
||||
new SeekPosition(
|
||||
new MediaSourceList.PlaylistTimeline(
|
||||
new PlaylistTimeline(
|
||||
mediaSourceListUpdateMessage.mediaSourceHolders,
|
||||
mediaSourceListUpdateMessage.shuffleOrder),
|
||||
mediaSourceListUpdateMessage.windowIndex,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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;
|
|||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
|
|
@ -234,7 +232,7 @@ import java.util.Set;
|
|||
int newEndIndex = newFromIndex + (toIndex - fromIndex) - 1;
|
||||
int endIndex = Math.max(newEndIndex, toIndex - 1);
|
||||
int windowOffset = mediaSourceHolders.get(startIndex).firstWindowIndexInChild;
|
||||
moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
|
||||
Util.moveItems(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
|
||||
for (int i = startIndex; i <= endIndex; i++) {
|
||||
MediaSourceHolder holder = mediaSourceHolders.get(i);
|
||||
holder.firstWindowIndexInChild = windowOffset;
|
||||
|
|
@ -472,18 +470,8 @@ import java.util.Set;
|
|||
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. */
|
||||
/* package */ static final class MediaSourceHolder {
|
||||
/* package */ static final class MediaSourceHolder implements MediaSourceInfoHolder {
|
||||
|
||||
public final MaskingMediaSource mediaSource;
|
||||
public final Object uid;
|
||||
|
|
@ -503,88 +491,15 @@ import java.util.Set;
|
|||
this.isRemoved = false;
|
||||
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
|
||||
public Object getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
@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;
|
||||
public Timeline getTimeline() {
|
||||
return mediaSource.getTimeline();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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> {
|
|||
}
|
||||
|
||||
/** Returns the {@link Timeline}. */
|
||||
public synchronized Timeline getTimeline() {
|
||||
public Timeline getTimeline() {
|
||||
return timeline;
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void onChildSourceInfoRefreshed(
|
||||
protected void onChildSourceInfoRefreshed(
|
||||
Void id, MediaSource mediaSource, Timeline newTimeline) {
|
||||
@Nullable MediaPeriodId idForMaskingPeriodPreparation = null;
|
||||
if (isPrepared) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue