NekoX/TMessagesProj/src/main/java/com/google/android/exoplayer2/DefaultMediaClock.java

198 lines
6.8 KiB
Java

/*
* 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 com.google.android.exoplayer2;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.MediaClock;
import 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()));
}
}