From f0420124954527e7f3eb529ca24f2a51dc7319f9 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 23 Feb 2023 16:02:14 +0000 Subject: [PATCH] Use ArrayDeque for pending output stream changes. The current logic uses manual array operations to keep track of pending changes. Modernize this code by using an ArrayDeque and a data class. This also allows to extend the output stream information in the future. This also fixes a bug where a position reset accidentally assigns a pending stream offset instead of keeping the current one. #minor-release PiperOrigin-RevId: 511787571 --- .../mediacodec/MediaCodecRenderer.java | 110 +++++++----------- 1 file changed, 42 insertions(+), 68 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index f78dd9d391..3b39a7e57a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -209,10 +209,6 @@ 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; - @Documented @Retention(RetentionPolicy.SOURCE) @Target(TYPE_USE) @@ -307,9 +303,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private final TimedValueQueue formatQueue; private final ArrayList decodeOnlyPresentationTimestamps; private final MediaCodec.BufferInfo outputBufferInfo; - private final long[] pendingOutputStreamStartPositionsUs; - private final long[] pendingOutputStreamOffsetsUs; - private final long[] pendingOutputStreamSwitchTimesUs; + private final ArrayDeque pendingOutputStreamChanges; private final OggOpusAudioPacketizer oggOpusAudioPacketizer; @Nullable private Format inputFormat; @@ -365,9 +359,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean pendingOutputEndOfStream; @Nullable private ExoPlaybackException pendingPlaybackException; protected DecoderCounters decoderCounters; - private long outputStreamStartPositionUs; - private long outputStreamOffsetUs; - private int pendingOutputStreamOffsetCount; + private OutputStreamInfo outputStreamInfo; /** * @param trackType The {@link C.TrackType track type} that the renderer handles. @@ -400,11 +392,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { currentPlaybackSpeed = 1f; targetPlaybackSpeed = 1f; renderTimeLimitMs = C.TIME_UNSET; - pendingOutputStreamStartPositionsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - outputStreamStartPositionUs = C.TIME_UNSET; - setOutputStreamOffsetUs(C.TIME_UNSET); + pendingOutputStreamChanges = new ArrayDeque<>(); + setOutputStreamInfo(OutputStreamInfo.UNSET); // MediaCodec outputs audio buffers in native endian: // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers // and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness. @@ -651,23 +640,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Override protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) throws ExoPlaybackException { - if (this.outputStreamOffsetUs == C.TIME_UNSET) { - checkState(this.outputStreamStartPositionUs == C.TIME_UNSET); - this.outputStreamStartPositionUs = startPositionUs; - setOutputStreamOffsetUs(offsetUs); + if (outputStreamInfo.streamOffsetUs == C.TIME_UNSET) { + checkState(outputStreamInfo.startPositionUs == C.TIME_UNSET); + setOutputStreamInfo( + new OutputStreamInfo( + /* previousStreamLastBufferTimeUs= */ C.TIME_UNSET, startPositionUs, offsetUs)); } else { - if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) { - Log.w( - TAG, - "Too many stream changes, so dropping offset: " - + pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]); - } else { - pendingOutputStreamOffsetCount++; - } - pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1] = startPositionUs; - pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs; - pendingOutputStreamSwitchTimesUs[pendingOutputStreamOffsetCount - 1] = - largestQueuedPresentationTimeUs; + pendingOutputStreamChanges.add( + new OutputStreamInfo(largestQueuedPresentationTimeUs, startPositionUs, offsetUs)); } } @@ -690,12 +670,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { waitingForFirstSampleInFormat = true; } formatQueue.clear(); - if (pendingOutputStreamOffsetCount != 0) { - setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]); - outputStreamStartPositionUs = - pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1]; - pendingOutputStreamOffsetCount = 0; - } + pendingOutputStreamChanges.clear(); } @Override @@ -709,9 +684,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Override protected void onDisabled() { inputFormat = null; - outputStreamStartPositionUs = C.TIME_UNSET; - setOutputStreamOffsetUs(C.TIME_UNSET); - pendingOutputStreamOffsetCount = 0; + setOutputStreamInfo(OutputStreamInfo.UNSET); + pendingOutputStreamChanges.clear(); flushOrReleaseCodec(); } @@ -1614,29 +1588,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ @CallSuper protected void onProcessedOutputBuffer(long presentationTimeUs) { - while (pendingOutputStreamOffsetCount != 0 - && presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) { - outputStreamStartPositionUs = pendingOutputStreamStartPositionsUs[0]; - setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[0]); - pendingOutputStreamOffsetCount--; - System.arraycopy( - pendingOutputStreamStartPositionsUs, - /* srcPos= */ 1, - pendingOutputStreamStartPositionsUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); - System.arraycopy( - pendingOutputStreamOffsetsUs, - /* srcPos= */ 1, - pendingOutputStreamOffsetsUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); - System.arraycopy( - pendingOutputStreamSwitchTimesUs, - /* srcPos= */ 1, - pendingOutputStreamSwitchTimesUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); + if (!pendingOutputStreamChanges.isEmpty() + && presentationTimeUs >= pendingOutputStreamChanges.peek().previousStreamLastBufferTimeUs) { + setOutputStreamInfo(pendingOutputStreamChanges.poll()); onProcessedStreamChange(); } } @@ -2083,13 +2037,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * boolean, Format)} to get the playback position with respect to the media. */ protected final long getOutputStreamOffsetUs() { - return outputStreamOffsetUs; + return outputStreamInfo.streamOffsetUs; } - private void setOutputStreamOffsetUs(long outputStreamOffsetUs) { - this.outputStreamOffsetUs = outputStreamOffsetUs; - if (outputStreamOffsetUs != C.TIME_UNSET) { - onOutputStreamOffsetUsChanged(outputStreamOffsetUs); + private void setOutputStreamInfo(OutputStreamInfo outputStreamInfo) { + this.outputStreamInfo = outputStreamInfo; + if (outputStreamInfo.streamOffsetUs != C.TIME_UNSET) { + onOutputStreamOffsetUsChanged(outputStreamInfo.streamOffsetUs); } } @@ -2543,6 +2497,26 @@ public abstract class MediaCodecRenderer extends BaseRenderer { && "OMX.MTK.AUDIO.DECODER.MP3".equals(name); } + private static final class OutputStreamInfo { + + public static final OutputStreamInfo UNSET = + new OutputStreamInfo( + /* previousStreamLastBufferTimeUs= */ C.TIME_UNSET, + /* startPositionUs= */ C.TIME_UNSET, + /* streamOffsetUs= */ C.TIME_UNSET); + + public final long previousStreamLastBufferTimeUs; + public final long startPositionUs; + public final long streamOffsetUs; + + public OutputStreamInfo( + long previousStreamLastBufferTimeUs, long startPositionUs, long streamOffsetUs) { + this.previousStreamLastBufferTimeUs = previousStreamLastBufferTimeUs; + this.startPositionUs = startPositionUs; + this.streamOffsetUs = streamOffsetUs; + } + } + @RequiresApi(31) private static final class Api31 { private Api31() {}