summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaSource.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaSource.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaSource.java184
1 files changed, 184 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaSource.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaSource.java
new file mode 100644
index 0000000000..ac2ef3c7da
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/MergingMediaSource.java
@@ -0,0 +1,184 @@
+/*
+ * 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.IntDef;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.Timeline;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.upstream.Allocator;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.upstream.TransferListener;
+import java.io.IOException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Merges multiple {@link MediaSource}s.
+ *
+ * <p>The {@link Timeline}s of the sources being merged must have the same number of periods.
+ */
+public final class MergingMediaSource extends CompositeMediaSource<Integer> {
+
+ /**
+ * Thrown when a {@link MergingMediaSource} cannot merge its sources.
+ */
+ public static final class IllegalMergeException extends IOException {
+
+ /** The reason the merge failed. One of {@link #REASON_PERIOD_COUNT_MISMATCH}. */
+ @Documented
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({REASON_PERIOD_COUNT_MISMATCH})
+ public @interface Reason {}
+ /**
+ * The sources have different period counts.
+ */
+ public static final int REASON_PERIOD_COUNT_MISMATCH = 0;
+
+ /**
+ * The reason the merge failed.
+ */
+ @Reason public final int reason;
+
+ /**
+ * @param reason The reason the merge failed.
+ */
+ public IllegalMergeException(@Reason int reason) {
+ this.reason = reason;
+ }
+
+ }
+
+ private static final int PERIOD_COUNT_UNSET = -1;
+
+ private final MediaSource[] mediaSources;
+ private final Timeline[] timelines;
+ private final ArrayList<MediaSource> pendingTimelineSources;
+ private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
+
+ private int periodCount;
+ @Nullable private IllegalMergeException mergeError;
+
+ /**
+ * @param mediaSources The {@link MediaSource}s to merge.
+ */
+ public MergingMediaSource(MediaSource... mediaSources) {
+ this(new DefaultCompositeSequenceableLoaderFactory(), mediaSources);
+ }
+
+ /**
+ * @param compositeSequenceableLoaderFactory A factory to create composite
+ * {@link SequenceableLoader}s for when this media source loads data from multiple streams
+ * (video, audio etc...).
+ * @param mediaSources The {@link MediaSource}s to merge.
+ */
+ public MergingMediaSource(CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
+ MediaSource... mediaSources) {
+ this.mediaSources = mediaSources;
+ this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
+ pendingTimelineSources = new ArrayList<>(Arrays.asList(mediaSources));
+ periodCount = PERIOD_COUNT_UNSET;
+ timelines = new Timeline[mediaSources.length];
+ }
+
+ @Override
+ @Nullable
+ public Object getTag() {
+ return mediaSources.length > 0 ? mediaSources[0].getTag() : null;
+ }
+
+ @Override
+ protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
+ super.prepareSourceInternal(mediaTransferListener);
+ for (int i = 0; i < mediaSources.length; i++) {
+ prepareChildSource(i, mediaSources[i]);
+ }
+ }
+
+ @Override
+ public void maybeThrowSourceInfoRefreshError() throws IOException {
+ if (mergeError != null) {
+ throw mergeError;
+ }
+ super.maybeThrowSourceInfoRefreshError();
+ }
+
+ @Override
+ public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
+ MediaPeriod[] periods = new MediaPeriod[mediaSources.length];
+ int periodIndex = timelines[0].getIndexOfPeriod(id.periodUid);
+ for (int i = 0; i < periods.length; i++) {
+ MediaPeriodId childMediaPeriodId =
+ id.copyWithPeriodUid(timelines[i].getUidOfPeriod(periodIndex));
+ periods[i] = mediaSources[i].createPeriod(childMediaPeriodId, allocator, startPositionUs);
+ }
+ return new MergingMediaPeriod(compositeSequenceableLoaderFactory, periods);
+ }
+
+ @Override
+ public void releasePeriod(MediaPeriod mediaPeriod) {
+ MergingMediaPeriod mergingPeriod = (MergingMediaPeriod) mediaPeriod;
+ for (int i = 0; i < mediaSources.length; i++) {
+ mediaSources[i].releasePeriod(mergingPeriod.periods[i]);
+ }
+ }
+
+ @Override
+ protected void releaseSourceInternal() {
+ super.releaseSourceInternal();
+ Arrays.fill(timelines, null);
+ periodCount = PERIOD_COUNT_UNSET;
+ mergeError = null;
+ pendingTimelineSources.clear();
+ Collections.addAll(pendingTimelineSources, mediaSources);
+ }
+
+ @Override
+ protected void onChildSourceInfoRefreshed(
+ Integer id, MediaSource mediaSource, Timeline timeline) {
+ if (mergeError == null) {
+ mergeError = checkTimelineMerges(timeline);
+ }
+ if (mergeError != null) {
+ return;
+ }
+ pendingTimelineSources.remove(mediaSource);
+ timelines[id] = timeline;
+ if (pendingTimelineSources.isEmpty()) {
+ refreshSourceInfo(timelines[0]);
+ }
+ }
+
+ @Override
+ @Nullable
+ protected MediaPeriodId getMediaPeriodIdForChildMediaPeriodId(
+ Integer id, MediaPeriodId mediaPeriodId) {
+ return id == 0 ? mediaPeriodId : null;
+ }
+
+ @Nullable
+ private IllegalMergeException checkTimelineMerges(Timeline timeline) {
+ if (periodCount == PERIOD_COUNT_UNSET) {
+ periodCount = timeline.getPeriodCount();
+ } else if (timeline.getPeriodCount() != periodCount) {
+ return new IllegalMergeException(IllegalMergeException.REASON_PERIOD_COUNT_MISMATCH);
+ }
+ return null;
+ }
+
+}