summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/DefaultMediaClock.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/DefaultMediaClock.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/DefaultMediaClock.java197
1 files changed, 197 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/DefaultMediaClock.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/DefaultMediaClock.java
new file mode 100644
index 0000000000..9967bfeb9e
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/DefaultMediaClock.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 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;
+
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Clock;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.MediaClock;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.StandaloneMediaClock;
+
+/**
+ * Default {@link MediaClock} which uses a renderer media clock and falls back to a
+ * {@link StandaloneMediaClock} if necessary.
+ */
+/* package */ final class DefaultMediaClock implements MediaClock {
+
+ /**
+ * Listener interface to be notified of changes to the active playback parameters.
+ */
+ public interface PlaybackParameterListener {
+
+ /**
+ * Called when the active playback parameters changed. Will not be called for {@link
+ * #setPlaybackParameters(PlaybackParameters)}.
+ *
+ * @param newPlaybackParameters The newly active {@link PlaybackParameters}.
+ */
+ void onPlaybackParametersChanged(PlaybackParameters newPlaybackParameters);
+ }
+
+ private final StandaloneMediaClock standaloneClock;
+ private final PlaybackParameterListener listener;
+
+ @Nullable private Renderer rendererClockSource;
+ @Nullable private MediaClock rendererClock;
+ private boolean isUsingStandaloneClock;
+ private boolean standaloneClockIsStarted;
+
+ /**
+ * Creates a new instance with listener for playback parameter changes and a {@link Clock} to use
+ * for the standalone clock implementation.
+ *
+ * @param listener A {@link PlaybackParameterListener} to listen for playback parameter
+ * changes.
+ * @param clock A {@link Clock}.
+ */
+ public DefaultMediaClock(PlaybackParameterListener listener, Clock clock) {
+ this.listener = listener;
+ this.standaloneClock = new StandaloneMediaClock(clock);
+ isUsingStandaloneClock = true;
+ }
+
+ /**
+ * Starts the standalone fallback clock.
+ */
+ public void start() {
+ standaloneClockIsStarted = true;
+ standaloneClock.start();
+ }
+
+ /**
+ * Stops the standalone fallback clock.
+ */
+ public void stop() {
+ standaloneClockIsStarted = false;
+ standaloneClock.stop();
+ }
+
+ /**
+ * Resets the position of the standalone fallback clock.
+ *
+ * @param positionUs The position to set in microseconds.
+ */
+ public void resetPosition(long positionUs) {
+ standaloneClock.resetPosition(positionUs);
+ }
+
+ /**
+ * Notifies the media clock that a renderer has been enabled. Starts using the media clock of the
+ * provided renderer if available.
+ *
+ * @param renderer The renderer which has been enabled.
+ * @throws ExoPlaybackException If the renderer provides a media clock and another renderer media
+ * clock is already provided.
+ */
+ public void onRendererEnabled(Renderer renderer) throws ExoPlaybackException {
+ MediaClock rendererMediaClock = renderer.getMediaClock();
+ if (rendererMediaClock != null && rendererMediaClock != rendererClock) {
+ if (rendererClock != null) {
+ throw ExoPlaybackException.createForUnexpected(
+ new IllegalStateException("Multiple renderer media clocks enabled."));
+ }
+ this.rendererClock = rendererMediaClock;
+ this.rendererClockSource = renderer;
+ rendererClock.setPlaybackParameters(standaloneClock.getPlaybackParameters());
+ }
+ }
+
+ /**
+ * Notifies the media clock that a renderer has been disabled. Stops using the media clock of this
+ * renderer if used.
+ *
+ * @param renderer The renderer which has been disabled.
+ */
+ public void onRendererDisabled(Renderer renderer) {
+ if (renderer == rendererClockSource) {
+ this.rendererClock = null;
+ this.rendererClockSource = null;
+ isUsingStandaloneClock = true;
+ }
+ }
+
+ /**
+ * Syncs internal clock if needed and returns current clock position in microseconds.
+ *
+ * @param isReadingAhead Whether the renderers are reading ahead.
+ */
+ public long syncAndGetPositionUs(boolean isReadingAhead) {
+ syncClocks(isReadingAhead);
+ return getPositionUs();
+ }
+
+ // MediaClock implementation.
+
+ @Override
+ public long getPositionUs() {
+ return isUsingStandaloneClock ? standaloneClock.getPositionUs() : rendererClock.getPositionUs();
+ }
+
+ @Override
+ public void setPlaybackParameters(PlaybackParameters playbackParameters) {
+ if (rendererClock != null) {
+ rendererClock.setPlaybackParameters(playbackParameters);
+ playbackParameters = rendererClock.getPlaybackParameters();
+ }
+ standaloneClock.setPlaybackParameters(playbackParameters);
+ }
+
+ @Override
+ public PlaybackParameters getPlaybackParameters() {
+ return rendererClock != null
+ ? rendererClock.getPlaybackParameters()
+ : standaloneClock.getPlaybackParameters();
+ }
+
+ private void syncClocks(boolean isReadingAhead) {
+ if (shouldUseStandaloneClock(isReadingAhead)) {
+ isUsingStandaloneClock = true;
+ if (standaloneClockIsStarted) {
+ standaloneClock.start();
+ }
+ return;
+ }
+ long rendererClockPositionUs = rendererClock.getPositionUs();
+ if (isUsingStandaloneClock) {
+ // Ensure enabling the renderer clock doesn't jump backwards in time.
+ if (rendererClockPositionUs < standaloneClock.getPositionUs()) {
+ standaloneClock.stop();
+ return;
+ }
+ isUsingStandaloneClock = false;
+ if (standaloneClockIsStarted) {
+ standaloneClock.start();
+ }
+ }
+ // Continuously sync stand-alone clock to renderer clock so that it can take over if needed.
+ standaloneClock.resetPosition(rendererClockPositionUs);
+ PlaybackParameters playbackParameters = rendererClock.getPlaybackParameters();
+ if (!playbackParameters.equals(standaloneClock.getPlaybackParameters())) {
+ standaloneClock.setPlaybackParameters(playbackParameters);
+ listener.onPlaybackParametersChanged(playbackParameters);
+ }
+ }
+
+ private boolean shouldUseStandaloneClock(boolean isReadingAhead) {
+ // Use the standalone clock if the clock providing renderer is not set or has ended. Also use
+ // the standalone clock if the renderer is not ready and we have finished reading the stream or
+ // are reading ahead to avoid getting stuck if tracks in the current period have uneven
+ // durations. See: https://github.com/google/ExoPlayer/issues/1874.
+ return rendererClockSource == null
+ || rendererClockSource.isEnded()
+ || (!rendererClockSource.isReady()
+ && (isReadingAhead || rendererClockSource.hasReadStreamToEnd()));
+ }
+}