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
This commit is contained in:
tonihei 2023-02-23 16:02:14 +00:00
parent ecf168b359
commit f042012495

View File

@ -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<Format> formatQueue;
private final ArrayList<Long> decodeOnlyPresentationTimestamps;
private final MediaCodec.BufferInfo outputBufferInfo;
private final long[] pendingOutputStreamStartPositionsUs;
private final long[] pendingOutputStreamOffsetsUs;
private final long[] pendingOutputStreamSwitchTimesUs;
private final ArrayDeque<OutputStreamInfo> 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() {}