Skip to content

Commit

Permalink
Rollback of b69b332
Browse files Browse the repository at this point in the history
*** Original commit ***

Mark output sample as decode-only based on start time

We currently do the same check on the input timestamps and
expect the output timestamps to match. Some codecs produce
samples with modified timestamps and the logic is a lot safer
when the comparison with the start time is done on the output
side of the codec.

Issue: google/ExoPlayer#11000

***

PiperOrigin-RevId: 543379665
  • Loading branch information
tonihei authored and tof-tof committed Jun 26, 2023
1 parent 10cf063 commit 7996766
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 112 deletions.
3 changes: 0 additions & 3 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
playback when the playback suppression due to no suitable output has
been enabled via
`ExoPlayer.Builder.setSuppressPlaybackOnUnsuitableOutput`.
* Fix seeking issues in AC4 streams caused by not identifying decode-only
samples correctly
([#11000](https://github.com/google/ExoPlayer/issues/11000)).
* Fix issue in `PlaybackStatsListener` where spurious `PlaybackStats` are
created after the playlist is cleared.
* Transformer:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public final void enable(
state = STATE_ENABLED;
onEnabled(joining, mayRenderStartOfStream);
replaceStream(formats, stream, startPositionUs, offsetUs);
resetPosition(startPositionUs, joining);
resetPosition(positionUs, joining);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;

/** An abstract renderer that uses {@link MediaCodec} to decode samples for rendering. */
Expand Down Expand Up @@ -299,6 +300,7 @@ private static String buildCustomDiagnosticInfo(int errorCode) {
private final DecoderInputBuffer buffer;
private final DecoderInputBuffer bypassSampleBuffer;
private final BatchBuffer bypassBatchBuffer;
private final ArrayList<Long> decodeOnlyPresentationTimestamps;
private final MediaCodec.BufferInfo outputBufferInfo;
private final ArrayDeque<OutputStreamInfo> pendingOutputStreamChanges;
private final OggOpusAudioPacketizer oggOpusAudioPacketizer;
Expand Down Expand Up @@ -399,6 +401,7 @@ public MediaCodecRenderer(
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
bypassSampleBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
bypassBatchBuffer = new BatchBuffer();
decodeOnlyPresentationTimestamps = new ArrayList<>();
outputBufferInfo = new MediaCodec.BufferInfo();
currentPlaybackSpeed = 1f;
targetPlaybackSpeed = 1f;
Expand Down Expand Up @@ -918,6 +921,7 @@ protected void resetCodecStateForFlush() {
shouldSkipAdaptationWorkaroundOutputBuffer = false;
isDecodeOnlyOutputBuffer = false;
isLastOutputBuffer = false;
decodeOnlyPresentationTimestamps.clear();
largestQueuedPresentationTimeUs = C.TIME_UNSET;
lastBufferInStreamPresentationTimeUs = C.TIME_UNSET;
lastProcessedOutputBufferTimeUs = C.TIME_UNSET;
Expand Down Expand Up @@ -1374,6 +1378,9 @@ private boolean feedInputBuffer() throws ExoPlaybackException {
c2Mp3TimestampTracker.getLastOutputBufferPresentationTimeUs(inputFormat));
}

if (buffer.isDecodeOnly()) {
decodeOnlyPresentationTimestamps.add(presentationTimeUs);
}
if (waitingForFirstSampleInFormat) {
if (!pendingOutputStreamChanges.isEmpty()) {
pendingOutputStreamChanges.peekLast().formatQueue.add(presentationTimeUs, inputFormat);
Expand Down Expand Up @@ -1903,7 +1910,7 @@ private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs)
&& largestQueuedPresentationTimeUs != C.TIME_UNSET) {
outputBufferInfo.presentationTimeUs = largestQueuedPresentationTimeUs;
}
isDecodeOnlyOutputBuffer = outputBufferInfo.presentationTimeUs < getLastResetPositionUs();
isDecodeOnlyOutputBuffer = isDecodeOnlyBuffer(outputBufferInfo.presentationTimeUs);
isLastOutputBuffer =
lastBufferInStreamPresentationTimeUs == outputBufferInfo.presentationTimeUs;
updateOutputFormatForTime(outputBufferInfo.presentationTimeUs);
Expand Down Expand Up @@ -2194,6 +2201,19 @@ private void reinitializeCodec() throws ExoPlaybackException {
maybeInitCodecOrBypass();
}

private boolean isDecodeOnlyBuffer(long presentationTimeUs) {
// We avoid using decodeOnlyPresentationTimestamps.remove(presentationTimeUs) because it would
// box presentationTimeUs, creating a Long object that would need to be garbage collected.
int size = decodeOnlyPresentationTimestamps.size();
for (int i = 0; i < size; i++) {
if (decodeOnlyPresentationTimestamps.get(i) == presentationTimeUs) {
decodeOnlyPresentationTimestamps.remove(i);
return true;
}
}
return false;
}

@RequiresApi(23)
private void updateDrmSessionV23() throws ExoPlaybackException {
CryptoConfig cryptoConfig = sourceDrmSession.getCryptoConfig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
Expand Down Expand Up @@ -326,90 +323,6 @@ public void render_withReplaceStream_triggersOutputCallbacksInCorrectOrder() thr
inOrder.verify(renderer).onProcessedOutputBuffer(400);
}

@Test
public void render_afterEnableWithStartPositionUs_skipsSamplesBeforeStartPositionUs()
throws Exception {
Format format =
new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build();
FakeSampleStream fakeSampleStream =
createFakeSampleStream(format, /* sampleTimesUs...= */ 0, 100, 200, 300, 400, 500);
MediaCodecRenderer renderer = spy(new TestRenderer());
renderer.init(/* index= */ 0, PlayerId.UNSET);

renderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {format},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 300,
/* offsetUs= */ 0);
renderer.start();
renderer.setCurrentStreamFinal();
long positionUs = 0;
while (!renderer.isEnded()) {
renderer.render(positionUs, SystemClock.elapsedRealtime());
positionUs += 100;
}

InOrder inOrder = inOrder(renderer);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 0, /* isDecodeOnly= */ true);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 100, /* isDecodeOnly= */ true);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 200, /* isDecodeOnly= */ true);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 300, /* isDecodeOnly= */ false);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 400, /* isDecodeOnly= */ false);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 500, /* isDecodeOnly= */ false);
}

@Test
public void render_afterPositionReset_skipsSamplesBeforeStartPositionUs() throws Exception {
Format format =
new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build();
FakeSampleStream fakeSampleStream =
createFakeSampleStream(format, /* sampleTimesUs...= */ 0, 100, 200, 300, 400, 500);
MediaCodecRenderer renderer = spy(new TestRenderer());
renderer.init(/* index= */ 0, PlayerId.UNSET);
renderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {format},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 400,
/* offsetUs= */ 0);
renderer.start();

renderer.resetPosition(/* positionUs= */ 200);
renderer.setCurrentStreamFinal();
long positionUs = 0;
while (!renderer.isEnded()) {
renderer.render(positionUs, SystemClock.elapsedRealtime());
positionUs += 100;
}

InOrder inOrder = inOrder(renderer);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 0, /* isDecodeOnly= */ true);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 100, /* isDecodeOnly= */ true);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 200, /* isDecodeOnly= */ false);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 300, /* isDecodeOnly= */ false);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 400, /* isDecodeOnly= */ false);
verifyProcessOutputBufferDecodeOnly(
inOrder, renderer, /* presentationTimeUs= */ 500, /* isDecodeOnly= */ false);
}

private FakeSampleStream createFakeSampleStream(Format format, long... sampleTimesUs) {
ImmutableList.Builder<FakeSampleStream.FakeSampleStreamItem> sampleListBuilder =
ImmutableList.builder();
Expand Down Expand Up @@ -516,23 +429,4 @@ protected DecoderReuseEvaluation canReuseCodec(
/* discardReasons= */ 0);
}
}

private static void verifyProcessOutputBufferDecodeOnly(
InOrder inOrder, MediaCodecRenderer renderer, long presentationTimeUs, boolean isDecodeOnly)
throws Exception {
inOrder
.verify(renderer)
.processOutputBuffer(
anyLong(),
anyLong(),
any(),
any(),
anyInt(),
anyInt(),
anyInt(),
eq(presentationTimeUs),
eq(isDecodeOnly),
anyBoolean(),
any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ public void render_withBufferLimitEqualToNumberOfSamples_rendersLastFrameAfterEn
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 30_000,
/* startPositionUs= */ 0,
/* offsetUs= */ 0);

mediaCodecVideoRenderer.start();
Expand Down

0 comments on commit 7996766

Please sign in to comment.