diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java index dcc066e9bb..41438e6d46 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java @@ -22,7 +22,6 @@ import android.media.MediaCrypto; import android.media.MediaFormat; import android.media.audiofx.Virtualizer; import android.os.Handler; -import androidx.annotation.CallSuper; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -43,7 +42,6 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer2.mediacodec.MediaFormatUtil; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MediaClock; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; @@ -71,13 +69,6 @@ import java.util.List; */ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock { - /** - * Maximum number of tracked pending stream change times. Generally there is zero or one pending - * stream change. We track more to allow for pending changes that have fewer samples than the - * codec latency. - */ - private static final int MAX_PENDING_STREAM_CHANGE_COUNT = 10; - private static final String TAG = "MediaCodecAudioRenderer"; /** * Custom key used to indicate bits per sample by some decoders on Vivo devices. For example @@ -88,7 +79,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media private final Context context; private final EventDispatcher eventDispatcher; private final AudioSink audioSink; - private final long[] pendingStreamChangeTimesUs; private int codecMaxInputSize; private boolean passthroughEnabled; @@ -99,8 +89,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media private long currentPositionUs; private boolean allowFirstBufferPositionDiscontinuity; private boolean allowPositionDiscontinuity; - private long lastInputTimeUs; - private int pendingStreamChangeCount; /** * @param context A context. @@ -354,8 +342,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media /* assumedMinimumCodecOperatingRate= */ 44100); this.context = context.getApplicationContext(); this.audioSink = audioSink; - lastInputTimeUs = C.TIME_UNSET; - pendingStreamChangeTimesUs = new long[MAX_PENDING_STREAM_CHANGE_COUNT]; eventDispatcher = new EventDispatcher(eventHandler, eventListener); audioSink.setListener(new AudioSinkListener()); } @@ -666,22 +652,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } } - @Override - protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException { - super.onStreamChanged(formats, offsetUs); - if (lastInputTimeUs != C.TIME_UNSET) { - if (pendingStreamChangeCount == pendingStreamChangeTimesUs.length) { - Log.w( - TAG, - "Too many stream changes, so dropping change at " - + pendingStreamChangeTimesUs[pendingStreamChangeCount - 1]); - } else { - pendingStreamChangeCount++; - } - pendingStreamChangeTimesUs[pendingStreamChangeCount - 1] = lastInputTimeUs; - } - } - @Override protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { super.onPositionReset(positionUs, joining); @@ -689,8 +659,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media currentPositionUs = positionUs; allowFirstBufferPositionDiscontinuity = true; allowPositionDiscontinuity = true; - lastInputTimeUs = C.TIME_UNSET; - pendingStreamChangeCount = 0; } @Override @@ -709,8 +677,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media @Override protected void onDisabled() { try { - lastInputTimeUs = C.TIME_UNSET; - pendingStreamChangeCount = 0; audioSink.flush(); } finally { try { @@ -769,22 +735,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } allowFirstBufferPositionDiscontinuity = false; } - lastInputTimeUs = Math.max(buffer.timeUs, lastInputTimeUs); } - @CallSuper @Override - protected void onProcessedOutputBuffer(long presentationTimeUs) { - while (pendingStreamChangeCount != 0 && presentationTimeUs >= pendingStreamChangeTimesUs[0]) { - audioSink.handleDiscontinuity(); - pendingStreamChangeCount--; - System.arraycopy( - pendingStreamChangeTimesUs, - /* srcPos= */ 1, - pendingStreamChangeTimesUs, - /* destPos= */ 0, - pendingStreamChangeCount); - } + protected void onProcessedStreamChange() { + super.onProcessedStreamChange(); + audioSink.handleDiscontinuity(); } @Override @@ -803,8 +759,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media if (codecNeedsEosBufferTimestampWorkaround && bufferPresentationTimeUs == 0 && (bufferFlags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 - && lastInputTimeUs != C.TIME_UNSET) { - bufferPresentationTimeUs = lastInputTimeUs; + && getLargestQueuedPresentationTimeUs() != C.TIME_UNSET) { + bufferPresentationTimeUs = getLargestQueuedPresentationTimeUs(); } if (passthroughEnabled && (bufferFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 6eec87e407..5cd4147b79 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -262,6 +262,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ private static final long MAX_CODEC_HOTSWAP_TIME_MS = 1000; + // Generally there is zero or one pending output stream offset. We track more offsets to allow for + // pending output streams that have fewer frames than the codec latency. + private static final int MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT = 10; + /** * The possible return values for {@link #canKeepCodec(MediaCodec, MediaCodecInfo, Format, * Format)}. @@ -384,6 +388,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private final TimedValueQueue formatQueue; private final ArrayList decodeOnlyPresentationTimestamps; private final MediaCodec.BufferInfo outputBufferInfo; + private final long[] pendingOutputStreamOffsetsUs; + private final long[] pendingOutputStreamSwitchTimesUs; private boolean drmResourcesAcquired; @Nullable private Format inputFormat; @@ -435,6 +441,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean pendingOutputEndOfStream; @MediaCodecOperationMode private int mediaCodecOperationMode; protected DecoderCounters decoderCounters; + private long outputStreamOffsetUs; + private int pendingOutputStreamOffsetCount; /** * @param trackType The track type that the renderer handles. One of the {@code C.TRACK_TYPE_*} @@ -475,6 +483,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { rendererOperatingRate = 1f; renderTimeLimitMs = C.TIME_UNSET; mediaCodecOperationMode = OPERATION_MODE_SYNCHRONOUS; + pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; + pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; + outputStreamOffsetUs = C.TIME_UNSET; resetCodecStateForRelease(); } @@ -687,6 +698,25 @@ public abstract class MediaCodecRenderer extends BaseRenderer { decoderCounters = new DecoderCounters(); } + @Override + protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException { + if (outputStreamOffsetUs == C.TIME_UNSET) { + outputStreamOffsetUs = offsetUs; + } else { + if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) { + Log.w( + TAG, + "Too many stream changes, so dropping offset: " + + pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]); + } else { + pendingOutputStreamOffsetCount++; + } + pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs; + pendingOutputStreamSwitchTimesUs[pendingOutputStreamOffsetCount - 1] = + largestQueuedPresentationTimeUs; + } + } + @Override protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { inputStreamEnded = false; @@ -700,6 +730,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { waitingForFirstSampleInFormat = true; } formatQueue.clear(); + if (pendingOutputStreamOffsetCount != 0) { + outputStreamOffsetUs = pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]; + pendingOutputStreamOffsetCount = 0; + } } @Override @@ -715,6 +749,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Override protected void onDisabled() { inputFormat = null; + outputStreamOffsetUs = C.TIME_UNSET; + pendingOutputStreamOffsetCount = 0; if (sourceDrmSession != null || codecDrmSession != null) { // TODO: Do something better with this case. onReset(); @@ -1473,12 +1509,33 @@ public abstract class MediaCodecRenderer extends BaseRenderer { /** * Called when an output buffer is successfully processed. - *

- * The default implementation is a no-op. * * @param presentationTimeUs The timestamp associated with the output buffer. */ + @CallSuper protected void onProcessedOutputBuffer(long presentationTimeUs) { + while (pendingOutputStreamOffsetCount != 0 + && presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) { + outputStreamOffsetUs = pendingOutputStreamOffsetsUs[0]; + pendingOutputStreamOffsetCount--; + System.arraycopy( + pendingOutputStreamOffsetsUs, + /* srcPos= */ 1, + pendingOutputStreamOffsetsUs, + /* destPos= */ 0, + pendingOutputStreamOffsetCount); + System.arraycopy( + pendingOutputStreamSwitchTimesUs, + /* srcPos= */ 1, + pendingOutputStreamSwitchTimesUs, + /* destPos= */ 0, + pendingOutputStreamOffsetCount); + onProcessedStreamChange(); + } + } + + /** Called after the last output buffer before a stream change has been processed. */ + protected void onProcessedStreamChange() { // Do nothing. } @@ -1833,6 +1890,20 @@ public abstract class MediaCodecRenderer extends BaseRenderer { pendingOutputEndOfStream = true; } + /** Returns the largest queued input presentation time, in microseconds. */ + protected final long getLargestQueuedPresentationTimeUs() { + return largestQueuedPresentationTimeUs; + } + + /** + * Returns the offset that should be subtracted from {@code bufferPresentationTimeUs} in {@link + * #processOutputBuffer(long, long, MediaCodec, ByteBuffer, int, int, long, boolean, boolean, + * Format)} to get the playback position with respect to the media. + */ + protected final long getOutputStreamOffsetUs() { + return outputStreamOffsetUs; + } + private void reinitializeCodec() throws ExoPlaybackException { releaseCodec(); maybeInitCodec(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 157f5aab01..4bab19a355 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -87,9 +87,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private static final int[] STANDARD_LONG_EDGE_VIDEO_PX = new int[] { 1920, 1600, 1440, 1280, 960, 854, 640, 540, 480}; - // Generally there is zero or one pending output stream offset. We track more offsets to allow for - // pending output streams that have fewer frames than the codec latency. - private static final int MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT = 10; /** * Scale factor for the initial maximum input size used to configure the codec in non-adaptive * playbacks. See {@link #getCodecMaxValues(MediaCodecInfo, Format, Format[])}. @@ -125,8 +122,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private final long allowedJoiningTimeMs; private final int maxDroppedFramesToNotify; private final boolean deviceNeedsNoPostProcessWorkaround; - private final long[] pendingOutputStreamOffsetsUs; - private final long[] pendingOutputStreamSwitchTimesUs; private CodecMaxValues codecMaxValues; private boolean codecNeedsSetOutputSurfaceWorkaround; @@ -159,10 +154,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private boolean tunneling; private int tunnelingAudioSessionId; /* package */ @Nullable OnFrameRenderedListenerV23 tunnelingOnFrameRenderedListener; - - private long lastInputTimeUs; - private long outputStreamOffsetUs; - private int pendingOutputStreamOffsetCount; @Nullable private VideoFrameMetadataListener frameMetadataListener; /** @@ -347,10 +338,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(this.context); eventDispatcher = new EventDispatcher(eventHandler, eventListener); deviceNeedsNoPostProcessWorkaround = deviceNeedsNoPostProcessWorkaround(); - pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - outputStreamOffsetUs = C.TIME_UNSET; - lastInputTimeUs = C.TIME_UNSET; joiningDeadlineMs = C.TIME_UNSET; currentWidth = Format.NO_VALUE; currentHeight = Format.NO_VALUE; @@ -484,34 +471,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { frameReleaseTimeHelper.enable(); } - @Override - protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException { - if (outputStreamOffsetUs == C.TIME_UNSET) { - outputStreamOffsetUs = offsetUs; - } else { - if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) { - Log.w(TAG, "Too many stream changes, so dropping offset: " - + pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]); - } else { - pendingOutputStreamOffsetCount++; - } - pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs; - pendingOutputStreamSwitchTimesUs[pendingOutputStreamOffsetCount - 1] = lastInputTimeUs; - } - super.onStreamChanged(formats, offsetUs); - } - @Override protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { super.onPositionReset(positionUs, joining); clearRenderedFirstFrame(); initialPositionUs = C.TIME_UNSET; consecutiveDroppedFrameCount = 0; - lastInputTimeUs = C.TIME_UNSET; - if (pendingOutputStreamOffsetCount != 0) { - outputStreamOffsetUs = pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]; - pendingOutputStreamOffsetCount = 0; - } if (joining) { setJoiningDeadlineMs(); } else { @@ -556,9 +521,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @Override protected void onDisabled() { - lastInputTimeUs = C.TIME_UNSET; - outputStreamOffsetUs = C.TIME_UNSET; - pendingOutputStreamOffsetCount = 0; currentMediaFormat = null; clearReportedVideoSize(); clearRenderedFirstFrame(); @@ -759,7 +721,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { if (!tunneling) { buffersInCodecCount++; } - lastInputTimeUs = Math.max(buffer.timeUs, lastInputTimeUs); if (Util.SDK_INT < 23 && tunneling) { // In tunneled mode before API 23 we don't have a way to know when the buffer is output, so // treat it as if it were output immediately. @@ -838,6 +799,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { initialPositionUs = positionUs; } + long outputStreamOffsetUs = getOutputStreamOffsetUs(); long presentationTimeUs = bufferPresentationTimeUs - outputStreamOffsetUs; if (isDecodeOnlyBuffer && !isLastBuffer) { @@ -970,15 +932,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } } - /** - * Returns the offset that should be subtracted from {@code bufferPresentationTimeUs} in {@link - * #processOutputBuffer(long, long, MediaCodec, ByteBuffer, int, int, long, boolean, boolean, - * Format)} to get the playback position with respect to the media. - */ - protected long getOutputStreamOffsetUs() { - return outputStreamOffsetUs; - } - /** Called when a buffer was processed in tunneling mode. */ protected void onProcessedTunneledBuffer(long presentationTimeUs) { @Nullable Format format = updateOutputFormatForTime(presentationTimeUs); @@ -995,35 +948,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { setPendingOutputEndOfStream(); } - /** - * Called when an output buffer is successfully processed. - * - * @param presentationTimeUs The timestamp associated with the output buffer. - */ @CallSuper @Override protected void onProcessedOutputBuffer(long presentationTimeUs) { + super.onProcessedOutputBuffer(presentationTimeUs); if (!tunneling) { buffersInCodecCount--; } - while (pendingOutputStreamOffsetCount != 0 - && presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) { - outputStreamOffsetUs = pendingOutputStreamOffsetsUs[0]; - pendingOutputStreamOffsetCount--; - System.arraycopy( - pendingOutputStreamOffsetsUs, - /* srcPos= */ 1, - pendingOutputStreamOffsetsUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); - System.arraycopy( - pendingOutputStreamSwitchTimesUs, - /* srcPos= */ 1, - pendingOutputStreamSwitchTimesUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); - clearRenderedFirstFrame(); - } + } + + @Override + protected void onProcessedStreamChange() { + super.onProcessedStreamChange(); + clearRenderedFirstFrame(); } /**