Send decode-only Opus samples in bypass mode for seekPreRoll skip

As Opus decoders skip some bytes prior to playback during a seek, the renderer for bypass playback should send samples to the decoder even if they would be decode-only.

#minor-release

PiperOrigin-RevId: 574494666
(cherry picked from commit 00193e0304a5ea2c20012fabf77f82f29e218372)
This commit is contained in:
michaelkatz 2023-10-18 09:09:03 -07:00 committed by Rohit Singh
parent fe60d0d7b4
commit 111ac63695
3 changed files with 63 additions and 23 deletions

View File

@ -2279,7 +2279,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
// Process any batched data.
checkState(!outputStreamEnded);
if (bypassBatchBuffer.hasSamples()) {
boolean isDecodeOnly = bypassBatchBuffer.getLastSampleTimeUs() < getLastResetPositionUs();
boolean isDecodeOnly =
bypassBatchBuffer.getLastSampleTimeUs() < getLastResetPositionUs()
&& (outputFormat == null
|| !Objects.equals(outputFormat.sampleMimeType, MimeTypes.AUDIO_OPUS));
if (processOutputBuffer(
positionUs,
elapsedRealtimeUs,

View File

@ -15,7 +15,7 @@
*/
package androidx.media3.exoplayer.e2etest;
import static androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED;
import static androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_REQUIRED;
import android.content.Context;
import androidx.annotation.Nullable;
@ -40,12 +40,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class OggOpusPlaybackTest {
public final class OggOpusPlaybackTest {
public static final String INPUT_FILE = "bear.opus";
@ -53,25 +54,31 @@ public class OggOpusPlaybackTest {
public ShadowMediaCodecConfig mediaCodecConfig =
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
@Test
public void checkOggOpusEncodings() throws Exception {
Context applicationContext = ApplicationProvider.getApplicationContext();
OffloadRenderersFactory offloadRenderersFactory =
new OffloadRenderersFactory(applicationContext);
DefaultTrackSelector trackSelector = new DefaultTrackSelector(applicationContext);
public FakeClock fakeClock;
public OffloadRenderersFactory offloadRenderersFactory;
public DefaultTrackSelector trackSelector;
@Before
public void setUp() {
fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
offloadRenderersFactory =
new OffloadRenderersFactory(ApplicationProvider.getApplicationContext());
trackSelector = new DefaultTrackSelector(ApplicationProvider.getApplicationContext());
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAudioOffloadPreferences(
new AudioOffloadPreferences.Builder()
.setAudioOffloadMode(AUDIO_OFFLOAD_MODE_ENABLED)
.setIsGaplessSupportRequired(false)
.setIsSpeedChangeSupportRequired(false)
.setAudioOffloadMode(AUDIO_OFFLOAD_MODE_REQUIRED)
.build())
.build());
}
@Test
public void oggOpusPlayback_generatesCorrectOggOpusEncodings() throws Exception {
ExoPlayer player =
new ExoPlayer.Builder(applicationContext, offloadRenderersFactory)
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
new ExoPlayer.Builder(ApplicationProvider.getApplicationContext(), offloadRenderersFactory)
.setClock(fakeClock)
.setTrackSelector(trackSelector)
.build();
player.setMediaItem(MediaItem.fromUri("asset:///media/ogg/" + INPUT_FILE));
@ -82,12 +89,33 @@ public class OggOpusPlaybackTest {
player.release();
DumpFileAsserts.assertOutput(
applicationContext,
ApplicationProvider.getApplicationContext(),
offloadRenderersFactory,
"playbackdumps/ogg/" + INPUT_FILE + ".oggOpus.dump");
}
private static class OffloadRenderersFactory extends DefaultRenderersFactory
@Test
public void oggOpusPlayback_withSeek_generatesCorrectOggOpusEncodings() throws Exception {
ExoPlayer player =
new ExoPlayer.Builder(ApplicationProvider.getApplicationContext(), offloadRenderersFactory)
.setClock(fakeClock)
.setTrackSelector(trackSelector)
.build();
player.setMediaItem(MediaItem.fromUri("asset:///media/ogg/" + INPUT_FILE));
player.prepare();
player.seekTo(/* positionMs= */ 1415);
player.play();
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
player.release();
DumpFileAsserts.assertOutput(
ApplicationProvider.getApplicationContext(),
offloadRenderersFactory,
"playbackdumps/ogg/" + INPUT_FILE + ".oggOpusWithSeek.dump");
}
private static final class OffloadRenderersFactory extends DefaultRenderersFactory
implements Dumper.Dumpable {
private DumpingAudioSink dumpingAudioSink;
@ -117,7 +145,8 @@ public class OggOpusPlaybackTest {
}
}
private static class DumpingAudioSink extends ForwardingAudioSink implements Dumper.Dumpable {
private static final class DumpingAudioSink extends ForwardingAudioSink
implements Dumper.Dumpable {
/** All handleBuffer interactions recorded with this audio sink. */
private final List<CapturedInputBuffer> capturedInteractions;
@ -174,14 +203,14 @@ public class OggOpusPlaybackTest {
buffer.position(originalPosition);
return bytes;
}
}
/** Data record */
private static class CapturedInputBuffer {
private final byte[] contents;
/** Data record */
private static final class CapturedInputBuffer {
private final byte[] contents;
private CapturedInputBuffer(byte[] contents) {
this.contents = contents;
private CapturedInputBuffer(byte[] contents) {
this.contents = contents;
}
}
}
}

View File

@ -0,0 +1,8 @@
SinkDump (OggOpus):
buffers.length = 6
buffers[0] = length 207, hash D462AF66
buffers[1] = length 3891, hash FE9EE7C1
buffers[2] = length 3732, hash C2249BC1
buffers[3] = length 3731, hash A9384B0F
buffers[4] = length 4091, hash 9631FA86
buffers[5] = length 776, hash 4BC27E65