summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaPeriod.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaPeriod.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaPeriod.java256
1 files changed, 256 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaPeriod.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaPeriod.java
new file mode 100644
index 0000000000..f3315ec5cd
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaPeriod.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.source;
+
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.SeekParameters;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.trackselection.TrackSelection;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import org.checkerframework.checker.nullness.compatqual.NullableType;
+
+/**
+ * Merges multiple {@link MediaPeriod}s.
+ */
+/* package */ final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callback {
+
+ public final MediaPeriod[] periods;
+
+ private final IdentityHashMap<SampleStream, Integer> streamPeriodIndices;
+ private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
+ private final ArrayList<MediaPeriod> childrenPendingPreparation;
+
+ @Nullable private Callback callback;
+ @Nullable private TrackGroupArray trackGroups;
+ private MediaPeriod[] enabledPeriods;
+ private SequenceableLoader compositeSequenceableLoader;
+
+ public MergingMediaPeriod(CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
+ MediaPeriod... periods) {
+ this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
+ this.periods = periods;
+ childrenPendingPreparation = new ArrayList<>();
+ compositeSequenceableLoader =
+ compositeSequenceableLoaderFactory.createCompositeSequenceableLoader();
+ streamPeriodIndices = new IdentityHashMap<>();
+ enabledPeriods = new MediaPeriod[0];
+ }
+
+ @Override
+ public void prepare(Callback callback, long positionUs) {
+ this.callback = callback;
+ Collections.addAll(childrenPendingPreparation, periods);
+ for (MediaPeriod period : periods) {
+ period.prepare(this, positionUs);
+ }
+ }
+
+ @Override
+ public void maybeThrowPrepareError() throws IOException {
+ for (MediaPeriod period : periods) {
+ period.maybeThrowPrepareError();
+ }
+ }
+
+ @Override
+ public TrackGroupArray getTrackGroups() {
+ return Assertions.checkNotNull(trackGroups);
+ }
+
+ @Override
+ public long selectTracks(
+ @NullableType TrackSelection[] selections,
+ boolean[] mayRetainStreamFlags,
+ @NullableType SampleStream[] streams,
+ boolean[] streamResetFlags,
+ long positionUs) {
+ // Map each selection and stream onto a child period index.
+ int[] streamChildIndices = new int[selections.length];
+ int[] selectionChildIndices = new int[selections.length];
+ for (int i = 0; i < selections.length; i++) {
+ streamChildIndices[i] = streams[i] == null ? C.INDEX_UNSET
+ : streamPeriodIndices.get(streams[i]);
+ selectionChildIndices[i] = C.INDEX_UNSET;
+ if (selections[i] != null) {
+ TrackGroup trackGroup = selections[i].getTrackGroup();
+ for (int j = 0; j < periods.length; j++) {
+ if (periods[j].getTrackGroups().indexOf(trackGroup) != C.INDEX_UNSET) {
+ selectionChildIndices[i] = j;
+ break;
+ }
+ }
+ }
+ }
+ streamPeriodIndices.clear();
+ // Select tracks for each child, copying the resulting streams back into a new streams array.
+ @NullableType SampleStream[] newStreams = new SampleStream[selections.length];
+ @NullableType SampleStream[] childStreams = new SampleStream[selections.length];
+ @NullableType TrackSelection[] childSelections = new TrackSelection[selections.length];
+ ArrayList<MediaPeriod> enabledPeriodsList = new ArrayList<>(periods.length);
+ for (int i = 0; i < periods.length; i++) {
+ for (int j = 0; j < selections.length; j++) {
+ childStreams[j] = streamChildIndices[j] == i ? streams[j] : null;
+ childSelections[j] = selectionChildIndices[j] == i ? selections[j] : null;
+ }
+ long selectPositionUs = periods[i].selectTracks(childSelections, mayRetainStreamFlags,
+ childStreams, streamResetFlags, positionUs);
+ if (i == 0) {
+ positionUs = selectPositionUs;
+ } else if (selectPositionUs != positionUs) {
+ throw new IllegalStateException("Children enabled at different positions.");
+ }
+ boolean periodEnabled = false;
+ for (int j = 0; j < selections.length; j++) {
+ if (selectionChildIndices[j] == i) {
+ // Assert that the child provided a stream for the selection.
+ SampleStream childStream = Assertions.checkNotNull(childStreams[j]);
+ newStreams[j] = childStreams[j];
+ periodEnabled = true;
+ streamPeriodIndices.put(childStream, i);
+ } else if (streamChildIndices[j] == i) {
+ // Assert that the child cleared any previous stream.
+ Assertions.checkState(childStreams[j] == null);
+ }
+ }
+ if (periodEnabled) {
+ enabledPeriodsList.add(periods[i]);
+ }
+ }
+ // Copy the new streams back into the streams array.
+ System.arraycopy(newStreams, 0, streams, 0, newStreams.length);
+ // Update the local state.
+ enabledPeriods = new MediaPeriod[enabledPeriodsList.size()];
+ enabledPeriodsList.toArray(enabledPeriods);
+ compositeSequenceableLoader =
+ compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(enabledPeriods);
+ return positionUs;
+ }
+
+ @Override
+ public void discardBuffer(long positionUs, boolean toKeyframe) {
+ for (MediaPeriod period : enabledPeriods) {
+ period.discardBuffer(positionUs, toKeyframe);
+ }
+ }
+
+ @Override
+ public void reevaluateBuffer(long positionUs) {
+ compositeSequenceableLoader.reevaluateBuffer(positionUs);
+ }
+
+ @Override
+ public boolean continueLoading(long positionUs) {
+ if (!childrenPendingPreparation.isEmpty()) {
+ // Preparation is still going on.
+ int childrenPendingPreparationSize = childrenPendingPreparation.size();
+ for (int i = 0; i < childrenPendingPreparationSize; i++) {
+ childrenPendingPreparation.get(i).continueLoading(positionUs);
+ }
+ return false;
+ } else {
+ return compositeSequenceableLoader.continueLoading(positionUs);
+ }
+ }
+
+ @Override
+ public boolean isLoading() {
+ return compositeSequenceableLoader.isLoading();
+ }
+
+ @Override
+ public long getNextLoadPositionUs() {
+ return compositeSequenceableLoader.getNextLoadPositionUs();
+ }
+
+ @Override
+ public long readDiscontinuity() {
+ long positionUs = periods[0].readDiscontinuity();
+ // Periods other than the first one are not allowed to report discontinuities.
+ for (int i = 1; i < periods.length; i++) {
+ if (periods[i].readDiscontinuity() != C.TIME_UNSET) {
+ throw new IllegalStateException("Child reported discontinuity.");
+ }
+ }
+ // It must be possible to seek enabled periods to the new position, if there is one.
+ if (positionUs != C.TIME_UNSET) {
+ for (MediaPeriod enabledPeriod : enabledPeriods) {
+ if (enabledPeriod != periods[0]
+ && enabledPeriod.seekToUs(positionUs) != positionUs) {
+ throw new IllegalStateException("Unexpected child seekToUs result.");
+ }
+ }
+ }
+ return positionUs;
+ }
+
+ @Override
+ public long getBufferedPositionUs() {
+ return compositeSequenceableLoader.getBufferedPositionUs();
+ }
+
+ @Override
+ public long seekToUs(long positionUs) {
+ positionUs = enabledPeriods[0].seekToUs(positionUs);
+ // Additional periods must seek to the same position.
+ for (int i = 1; i < enabledPeriods.length; i++) {
+ if (enabledPeriods[i].seekToUs(positionUs) != positionUs) {
+ throw new IllegalStateException("Unexpected child seekToUs result.");
+ }
+ }
+ return positionUs;
+ }
+
+ @Override
+ public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
+ MediaPeriod queryPeriod = enabledPeriods.length > 0 ? enabledPeriods[0] : periods[0];
+ return queryPeriod.getAdjustedSeekPositionUs(positionUs, seekParameters);
+ }
+
+ // MediaPeriod.Callback implementation
+
+ @Override
+ public void onPrepared(MediaPeriod preparedPeriod) {
+ childrenPendingPreparation.remove(preparedPeriod);
+ if (!childrenPendingPreparation.isEmpty()) {
+ return;
+ }
+ int totalTrackGroupCount = 0;
+ for (MediaPeriod period : periods) {
+ totalTrackGroupCount += period.getTrackGroups().length;
+ }
+ TrackGroup[] trackGroupArray = new TrackGroup[totalTrackGroupCount];
+ int trackGroupIndex = 0;
+ for (MediaPeriod period : periods) {
+ TrackGroupArray periodTrackGroups = period.getTrackGroups();
+ int periodTrackGroupCount = periodTrackGroups.length;
+ for (int j = 0; j < periodTrackGroupCount; j++) {
+ trackGroupArray[trackGroupIndex++] = periodTrackGroups.get(j);
+ }
+ }
+ trackGroups = new TrackGroupArray(trackGroupArray);
+ Assertions.checkNotNull(callback).onPrepared(this);
+ }
+
+ @Override
+ public void onContinueLoadingRequested(MediaPeriod ignored) {
+ Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
+ }
+
+}