summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/BaseMediaSource.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/BaseMediaSource.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/BaseMediaSource.java191
1 files changed, 191 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/BaseMediaSource.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/BaseMediaSource.java
new file mode 100644
index 0000000000..f9ca6ff311
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/source/BaseMediaSource.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 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 android.os.Handler;
+import android.os.Looper;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.Timeline;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.upstream.TransferListener;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Base {@link MediaSource} implementation to handle parallel reuse and to keep a list of {@link
+ * MediaSourceEventListener}s.
+ *
+ * <p>Whenever an implementing subclass needs to provide a new timeline, it must call {@link
+ * #refreshSourceInfo(Timeline)} to notify all listeners.
+ */
+public abstract class BaseMediaSource implements MediaSource {
+
+ private final ArrayList<MediaSourceCaller> mediaSourceCallers;
+ private final HashSet<MediaSourceCaller> enabledMediaSourceCallers;
+ private final MediaSourceEventListener.EventDispatcher eventDispatcher;
+
+ @Nullable private Looper looper;
+ @Nullable private Timeline timeline;
+
+ public BaseMediaSource() {
+ mediaSourceCallers = new ArrayList<>(/* initialCapacity= */ 1);
+ enabledMediaSourceCallers = new HashSet<>(/* initialCapacity= */ 1);
+ eventDispatcher = new MediaSourceEventListener.EventDispatcher();
+ }
+
+ /**
+ * Starts source preparation and enables the source, see {@link #prepareSource(MediaSourceCaller,
+ * TransferListener)}. This method is called at most once until the next call to {@link
+ * #releaseSourceInternal()}.
+ *
+ * @param mediaTransferListener The transfer listener which should be informed of any media data
+ * transfers. May be null if no listener is available. Note that this listener should usually
+ * be only informed of transfers related to the media loads and not of auxiliary loads for
+ * manifests and other data.
+ */
+ protected abstract void prepareSourceInternal(@Nullable TransferListener mediaTransferListener);
+
+ /** Enables the source, see {@link #enable(MediaSourceCaller)}. */
+ protected void enableInternal() {}
+
+ /** Disables the source, see {@link #disable(MediaSourceCaller)}. */
+ protected void disableInternal() {}
+
+ /**
+ * Releases the source, see {@link #releaseSource(MediaSourceCaller)}. This method is called
+ * exactly once after each call to {@link #prepareSourceInternal(TransferListener)}.
+ */
+ protected abstract void releaseSourceInternal();
+
+ /**
+ * Updates timeline and manifest and notifies all listeners of the update.
+ *
+ * @param timeline The new {@link Timeline}.
+ */
+ protected final void refreshSourceInfo(Timeline timeline) {
+ this.timeline = timeline;
+ for (MediaSourceCaller caller : mediaSourceCallers) {
+ caller.onSourceInfoRefreshed(/* source= */ this, timeline);
+ }
+ }
+
+ /**
+ * Returns a {@link MediaSourceEventListener.EventDispatcher} which dispatches all events to the
+ * registered listeners with the specified media period id.
+ *
+ * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. May be null, if
+ * the events do not belong to a specific media period.
+ * @return An event dispatcher with pre-configured media period id.
+ */
+ protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
+ @Nullable MediaPeriodId mediaPeriodId) {
+ return eventDispatcher.withParameters(
+ /* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0);
+ }
+
+ /**
+ * Returns a {@link MediaSourceEventListener.EventDispatcher} which dispatches all events to the
+ * registered listeners with the specified media period id and time offset.
+ *
+ * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events.
+ * @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds.
+ * @return An event dispatcher with pre-configured media period id and time offset.
+ */
+ protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
+ MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
+ Assertions.checkArgument(mediaPeriodId != null);
+ return eventDispatcher.withParameters(/* windowIndex= */ 0, mediaPeriodId, mediaTimeOffsetMs);
+ }
+
+ /**
+ * Returns a {@link MediaSourceEventListener.EventDispatcher} which dispatches all events to the
+ * registered listeners with the specified window index, media period id and time offset.
+ *
+ * @param windowIndex The timeline window index to be reported with the events.
+ * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. May be null, if
+ * the events do not belong to a specific media period.
+ * @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds.
+ * @return An event dispatcher with pre-configured media period id and time offset.
+ */
+ protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
+ int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
+ return eventDispatcher.withParameters(windowIndex, mediaPeriodId, mediaTimeOffsetMs);
+ }
+
+ /** Returns whether the source is enabled. */
+ protected final boolean isEnabled() {
+ return !enabledMediaSourceCallers.isEmpty();
+ }
+
+ @Override
+ public final void addEventListener(Handler handler, MediaSourceEventListener eventListener) {
+ eventDispatcher.addEventListener(handler, eventListener);
+ }
+
+ @Override
+ public final void removeEventListener(MediaSourceEventListener eventListener) {
+ eventDispatcher.removeEventListener(eventListener);
+ }
+
+ @Override
+ public final void prepareSource(
+ MediaSourceCaller caller, @Nullable TransferListener mediaTransferListener) {
+ Looper looper = Looper.myLooper();
+ Assertions.checkArgument(this.looper == null || this.looper == looper);
+ Timeline timeline = this.timeline;
+ mediaSourceCallers.add(caller);
+ if (this.looper == null) {
+ this.looper = looper;
+ enabledMediaSourceCallers.add(caller);
+ prepareSourceInternal(mediaTransferListener);
+ } else if (timeline != null) {
+ enable(caller);
+ caller.onSourceInfoRefreshed(/* source= */ this, timeline);
+ }
+ }
+
+ @Override
+ public final void enable(MediaSourceCaller caller) {
+ Assertions.checkNotNull(looper);
+ boolean wasDisabled = enabledMediaSourceCallers.isEmpty();
+ enabledMediaSourceCallers.add(caller);
+ if (wasDisabled) {
+ enableInternal();
+ }
+ }
+
+ @Override
+ public final void disable(MediaSourceCaller caller) {
+ boolean wasEnabled = !enabledMediaSourceCallers.isEmpty();
+ enabledMediaSourceCallers.remove(caller);
+ if (wasEnabled && enabledMediaSourceCallers.isEmpty()) {
+ disableInternal();
+ }
+ }
+
+ @Override
+ public final void releaseSource(MediaSourceCaller caller) {
+ mediaSourceCallers.remove(caller);
+ if (mediaSourceCallers.isEmpty()) {
+ looper = null;
+ timeline = null;
+ enabledMediaSourceCallers.clear();
+ releaseSourceInternal();
+ } else {
+ disable(caller);
+ }
+ }
+}