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 {
......
...@@ -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