diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/EndToEndOffloadFailureRecoveryTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/EndToEndOffloadFailureRecoveryTest.java new file mode 100644 index 0000000000..59b7802917 --- /dev/null +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/EndToEndOffloadFailureRecoveryTest.java @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.e2etest; + +import static androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED; +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.media.AudioTrack; +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.Player; +import androidx.media3.common.TrackSelectionParameters; +import androidx.media3.exoplayer.DefaultRenderersFactory; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.exoplayer.audio.AudioOffloadSupport; +import androidx.media3.exoplayer.audio.AudioSink; +import androidx.media3.exoplayer.audio.DefaultAudioSink; +import androidx.media3.exoplayer.audio.ForwardingAudioSink; +import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; +import androidx.media3.test.utils.DumpFileAsserts; +import androidx.media3.test.utils.Dumper; +import androidx.media3.test.utils.FakeClock; +import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig; +import androidx.media3.test.utils.robolectric.TestPlayerRunHelper; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** End-to-end tests of playback with recovery from offload failure. */ +@RunWith(AndroidJUnit4.class) +public class EndToEndOffloadFailureRecoveryTest { + public static final String INPUT_FILE = "bear.opus"; + + @Rule + public ShadowMediaCodecConfig mediaCodecConfig = + ShadowMediaCodecConfig.forAllSupportedMimeTypes(); + + public FakeClock fakeClock; + public DefaultTrackSelector trackSelector; + + @Before + public void setUp() { + fakeClock = new FakeClock(/* isAutoAdvancing= */ true); + trackSelector = new DefaultTrackSelector(ApplicationProvider.getApplicationContext()); + trackSelector.setParameters( + trackSelector + .buildUponParameters() + .setAudioOffloadPreferences( + new TrackSelectionParameters.AudioOffloadPreferences.Builder() + .setAudioOffloadMode(AUDIO_OFFLOAD_MODE_ENABLED) + .build()) + .build()); + } + + @Test + public void oggOpusPlayback_recoversFromAudioTrackOffloadInitFailure_generatesDecodedContent() + throws Exception { + OffloadInitFailureRenderersFactory offloadInitFailureRenderersFactory = + new OffloadInitFailureRenderersFactory(ApplicationProvider.getApplicationContext()); + ExoPlayer player = + new ExoPlayer.Builder( + ApplicationProvider.getApplicationContext(), offloadInitFailureRenderersFactory) + .setClock(fakeClock) + .setTrackSelector(trackSelector) + .build(); + player.setMediaItem(MediaItem.fromUri("asset:///media/ogg/" + INPUT_FILE)); + player.prepare(); + player.play(); + + TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + DumpFileAsserts.assertOutput( + ApplicationProvider.getApplicationContext(), + offloadInitFailureRenderersFactory, + "playbackdumps/offloadRecovery/" + INPUT_FILE + ".offloadInitFailureRecovery.dump"); + } + + @Test + public void oggOpusPlayback_recoversFromAudioTrackOffloadWriteFailure_generatesCorrectContent() + throws Exception { + OffloadWriteFailureRenderersFactory offloadWriteFailureRenderersFactory = + new OffloadWriteFailureRenderersFactory(ApplicationProvider.getApplicationContext()); + ExoPlayer player = + new ExoPlayer.Builder( + ApplicationProvider.getApplicationContext(), offloadWriteFailureRenderersFactory) + .setClock(fakeClock) + .setTrackSelector(trackSelector) + .build(); + player.setMediaItem(MediaItem.fromUri("asset:///media/ogg/" + INPUT_FILE)); + player.prepare(); + player.play(); + + TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + DumpFileAsserts.assertOutput( + ApplicationProvider.getApplicationContext(), + offloadWriteFailureRenderersFactory, + "playbackdumps/offloadRecovery/" + INPUT_FILE + ".offloadWriteFailureRecovery.dump"); + } + + private static class OffloadRenderersFactory extends DefaultRenderersFactory + implements Dumper.Dumpable { + + protected DumpingAudioSink dumpingAudioSink; + + /** + * @param context A {@link Context}. + */ + public OffloadRenderersFactory(Context context) { + super(context); + } + + @Override + protected AudioSink buildAudioSink( + Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams) { + dumpingAudioSink = + new DumpingAudioSink( + new DefaultAudioSink.Builder(context) + .setEnableFloatOutput(enableFloatOutput) + .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) + .build()); + return dumpingAudioSink; + } + + @Override + public void dump(Dumper dumper) { + dumpingAudioSink.dump(dumper); + } + } + + private static final class OffloadInitFailureRenderersFactory extends OffloadRenderersFactory { + + /** + * @param context A {@link Context}. + */ + public OffloadInitFailureRenderersFactory(Context context) { + super(context); + } + + @Override + protected AudioSink buildAudioSink( + Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams) { + dumpingAudioSink = + new DumpingAudioSinkWithOffloadInitFailure( + new DefaultAudioSink.Builder(context) + .setEnableFloatOutput(enableFloatOutput) + .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) + .build()); + return dumpingAudioSink; + } + } + + private static final class OffloadWriteFailureRenderersFactory extends OffloadRenderersFactory { + + /** + * @param context A {@link Context}. + */ + public OffloadWriteFailureRenderersFactory(Context context) { + super(context); + } + + @Override + protected AudioSink buildAudioSink( + Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams) { + dumpingAudioSink = + new DumpingAudioSinkWithOffloadWriteFailure( + new DefaultAudioSink.Builder(context) + .setEnableFloatOutput(enableFloatOutput) + .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) + .build()); + return dumpingAudioSink; + } + } + + /** A dumping audio sink that fails to initialize for offloaded playback. */ + private static class DumpingAudioSink extends ForwardingAudioSink implements Dumper.Dumpable { + /** All handleBuffer interactions recorded with this audio sink. */ + protected final List capturedInteractions; + + /** + * If offload mode should be not supported until next {@linkplain + * DumpingAudioSink#configure(Format, int, int[]) configure} call. + */ + protected boolean offloadDisabledUntilNextConfiguration; + + /** If audio sink has been configured for offloaded playback. */ + protected boolean isOffloadMode; + + /** The {@link Format input format} for the audio sink. */ + @Nullable protected Format inputFormat; + + public DumpingAudioSink(AudioSink sink) { + super(sink); + this.capturedInteractions = new ArrayList<>(); + this.isOffloadMode = false; + this.offloadDisabledUntilNextConfiguration = false; + } + + @Override + public void configure( + Format inputFormat, int specifiedBufferSize, @Nullable int[] outputChannels) { + // Bypass configure of base DefaultAudioSink + isOffloadMode = !Objects.equals(inputFormat.sampleMimeType, MimeTypes.AUDIO_RAW); + offloadDisabledUntilNextConfiguration = false; + this.inputFormat = inputFormat; + } + + @Override + public boolean supportsFormat(Format format) { + return Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_RAW); + } + + @Override + public AudioOffloadSupport getFormatOffloadSupport(Format format) { + if (offloadDisabledUntilNextConfiguration) { + return AudioOffloadSupport.DEFAULT_UNSUPPORTED; + } + return new AudioOffloadSupport.Builder() + .setIsFormatSupported(true) + .setIsGaplessSupported(false) + .setIsSpeedChangeSupported(false) + .build(); + } + + /** + * Captures audio data in {@link DumpingAudioSink}. + * + * @param buffer The buffer containing audio data. + * @param presentationTimeUs The presentation timestamp of the buffer in microseconds. + * @param encodedAccessUnitCount The number of encoded access units in the buffer, or 1 if the + * buffer contains PCM audio. This allows batching multiple encoded access units in one + * buffer. + * @return {@code True} if buffer successfully written. + */ + @Override + public boolean handleBuffer( + ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount) + throws InitializationException, WriteException { + capturedInteractions.add( + new DumpingAudioSink.CapturedInputBuffer( + peekBytes(buffer, 0, buffer.limit() - buffer.position()))); + return true; + } + + @Override + public void reset() { + offloadDisabledUntilNextConfiguration = false; + } + + @Override + public void dump(Dumper dumper) { + dumper.startBlock("SinkDump (OggOpus)"); + dumper.add("buffers.length", capturedInteractions.size()); + for (int i = 0; i < capturedInteractions.size(); i++) { + dumper.add("buffers[" + i + "]", capturedInteractions.get(i).contents); + } + dumper.endBlock(); + } + + protected byte[] peekBytes(ByteBuffer buffer, int offset, int size) { + int originalPosition = buffer.position(); + buffer.position(offset); + byte[] bytes = new byte[size]; + buffer.get(bytes); + buffer.position(originalPosition); + return bytes; + } + + /** Data record. */ + private static final class CapturedInputBuffer { + private final byte[] contents; + + private CapturedInputBuffer(byte[] contents) { + this.contents = contents; + } + } + } + + private static final class DumpingAudioSinkWithOffloadInitFailure extends DumpingAudioSink { + + public DumpingAudioSinkWithOffloadInitFailure(AudioSink sink) { + super(sink); + } + + /** + * Captures audio data in {@link DumpingAudioSink}. + * + *

Method throws {@code InitializationException} when called if sink is configured for + * offloaded playback. + * + * @param buffer The buffer containing audio data. + * @param presentationTimeUs The presentation timestamp of the buffer in microseconds. + * @param encodedAccessUnitCount The number of encoded access units in the buffer, or 1 if the + * buffer contains PCM audio. This allows batching multiple encoded access units in one + * buffer. + * @return {@code True} if buffer successfully written. + * @throws InitializationException if configured for offloaded audio playback. + */ + @Override + public boolean handleBuffer( + ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount) + throws InitializationException { + if (isOffloadMode) { + // Models that AudioTrack initialization throws error if configured for offloaded playback. + offloadDisabledUntilNextConfiguration = true; + assertThat(inputFormat).isNotNull(); + throw new InitializationException( + AudioTrack.STATE_UNINITIALIZED, + /* sampleRate= */ 48_000, + /* channelConfig= */ 0, + /* bufferSize= */ C.LENGTH_UNSET, + inputFormat, + /* isRecoverable= */ true, + /* audioTrackException= */ null); + } + capturedInteractions.add( + new DumpingAudioSink.CapturedInputBuffer( + peekBytes(buffer, 0, buffer.limit() - buffer.position()))); + return true; + } + } + + /** + * A dumping audio sink that starts failing for offloaded playback after two successful writes. + */ + private static final class DumpingAudioSinkWithOffloadWriteFailure extends DumpingAudioSink { + + private int writeCounter; + private boolean hasThrownWriteException; + + public DumpingAudioSinkWithOffloadWriteFailure(AudioSink sink) { + super(sink); + this.writeCounter = 0; + this.hasThrownWriteException = false; + } + + /** + * Captures audio data in {@link DumpingAudioSink}. + * + *

Method throws {@code WriteException} when called if sink is configured for offloaded + * playback and after two successful writes. + * + * @param buffer The buffer containing audio data. + * @param presentationTimeUs The presentation timestamp of the buffer in microseconds. + * @param encodedAccessUnitCount The number of encoded access units in the buffer, or 1 if the + * buffer contains PCM audio. This allows batching multiple encoded access units in one + * buffer. + * @return {@code True} if buffer successfully written. + * @throws WriteException if configured for offloaded audio playback. + */ + @Override + public boolean handleBuffer( + ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount) + throws WriteException { + if (isOffloadMode && writeCounter > 1) { + // Models that AudioTrack write throws error if configured for offloaded playback. + if (hasThrownWriteException) { + assertThat(offloadDisabledUntilNextConfiguration).isFalse(); + offloadDisabledUntilNextConfiguration = true; + } + hasThrownWriteException = true; + assertThat(inputFormat).isNotNull(); + throw new WriteException( + AudioTrack.ERROR_DEAD_OBJECT, inputFormat, /* isRecoverable= */ true); + } + capturedInteractions.add( + new DumpingAudioSink.CapturedInputBuffer( + peekBytes(buffer, 0, buffer.limit() - buffer.position()))); + writeCounter++; + return true; + } + + @Override + public void reset() { + super.reset(); + writeCounter = 0; + } + } +} diff --git a/libraries/test_data/src/test/assets/playbackdumps/offloadRecovery/bear.opus.offloadInitFailureRecovery.dump b/libraries/test_data/src/test/assets/playbackdumps/offloadRecovery/bear.opus.offloadInitFailureRecovery.dump new file mode 100644 index 0000000000..78b568b987 --- /dev/null +++ b/libraries/test_data/src/test/assets/playbackdumps/offloadRecovery/bear.opus.offloadInitFailureRecovery.dump @@ -0,0 +1,277 @@ +SinkDump (OggOpus): + buffers.length = 275 + buffers[0] = length 0, hash 1 + buffers[1] = length 0, hash 1 + buffers[2] = length 0, hash 1 + buffers[3] = length 0, hash 1 + buffers[4] = length 0, hash 1 + buffers[5] = length 0, hash 1 + buffers[6] = length 0, hash 1 + buffers[7] = length 0, hash 1 + buffers[8] = length 0, hash 1 + buffers[9] = length 0, hash 1 + buffers[10] = length 0, hash 1 + buffers[11] = length 0, hash 1 + buffers[12] = length 0, hash 1 + buffers[13] = length 0, hash 1 + buffers[14] = length 0, hash 1 + buffers[15] = length 0, hash 1 + buffers[16] = length 0, hash 1 + buffers[17] = length 0, hash 1 + buffers[18] = length 0, hash 1 + buffers[19] = length 0, hash 1 + buffers[20] = length 0, hash 1 + buffers[21] = length 0, hash 1 + buffers[22] = length 0, hash 1 + buffers[23] = length 0, hash 1 + buffers[24] = length 0, hash 1 + buffers[25] = length 0, hash 1 + buffers[26] = length 0, hash 1 + buffers[27] = length 0, hash 1 + buffers[28] = length 0, hash 1 + buffers[29] = length 0, hash 1 + buffers[30] = length 0, hash 1 + buffers[31] = length 0, hash 1 + buffers[32] = length 0, hash 1 + buffers[33] = length 0, hash 1 + buffers[34] = length 0, hash 1 + buffers[35] = length 0, hash 1 + buffers[36] = length 0, hash 1 + buffers[37] = length 0, hash 1 + buffers[38] = length 0, hash 1 + buffers[39] = length 0, hash 1 + buffers[40] = length 0, hash 1 + buffers[41] = length 0, hash 1 + buffers[42] = length 0, hash 1 + buffers[43] = length 0, hash 1 + buffers[44] = length 0, hash 1 + buffers[45] = length 0, hash 1 + buffers[46] = length 0, hash 1 + buffers[47] = length 0, hash 1 + buffers[48] = length 0, hash 1 + buffers[49] = length 0, hash 1 + buffers[50] = length 0, hash 1 + buffers[51] = length 0, hash 1 + buffers[52] = length 0, hash 1 + buffers[53] = length 0, hash 1 + buffers[54] = length 0, hash 1 + buffers[55] = length 0, hash 1 + buffers[56] = length 0, hash 1 + buffers[57] = length 0, hash 1 + buffers[58] = length 0, hash 1 + buffers[59] = length 0, hash 1 + buffers[60] = length 0, hash 1 + buffers[61] = length 0, hash 1 + buffers[62] = length 0, hash 1 + buffers[63] = length 0, hash 1 + buffers[64] = length 0, hash 1 + buffers[65] = length 0, hash 1 + buffers[66] = length 0, hash 1 + buffers[67] = length 0, hash 1 + buffers[68] = length 0, hash 1 + buffers[69] = length 0, hash 1 + buffers[70] = length 0, hash 1 + buffers[71] = length 0, hash 1 + buffers[72] = length 0, hash 1 + buffers[73] = length 0, hash 1 + buffers[74] = length 0, hash 1 + buffers[75] = length 0, hash 1 + buffers[76] = length 0, hash 1 + buffers[77] = length 0, hash 1 + buffers[78] = length 0, hash 1 + buffers[79] = length 0, hash 1 + buffers[80] = length 0, hash 1 + buffers[81] = length 0, hash 1 + buffers[82] = length 0, hash 1 + buffers[83] = length 0, hash 1 + buffers[84] = length 0, hash 1 + buffers[85] = length 0, hash 1 + buffers[86] = length 0, hash 1 + buffers[87] = length 0, hash 1 + buffers[88] = length 0, hash 1 + buffers[89] = length 0, hash 1 + buffers[90] = length 0, hash 1 + buffers[91] = length 0, hash 1 + buffers[92] = length 0, hash 1 + buffers[93] = length 0, hash 1 + buffers[94] = length 0, hash 1 + buffers[95] = length 0, hash 1 + buffers[96] = length 0, hash 1 + buffers[97] = length 0, hash 1 + buffers[98] = length 0, hash 1 + buffers[99] = length 0, hash 1 + buffers[100] = length 0, hash 1 + buffers[101] = length 0, hash 1 + buffers[102] = length 0, hash 1 + buffers[103] = length 0, hash 1 + buffers[104] = length 0, hash 1 + buffers[105] = length 0, hash 1 + buffers[106] = length 0, hash 1 + buffers[107] = length 0, hash 1 + buffers[108] = length 0, hash 1 + buffers[109] = length 0, hash 1 + buffers[110] = length 0, hash 1 + buffers[111] = length 0, hash 1 + buffers[112] = length 0, hash 1 + buffers[113] = length 0, hash 1 + buffers[114] = length 0, hash 1 + buffers[115] = length 0, hash 1 + buffers[116] = length 0, hash 1 + buffers[117] = length 0, hash 1 + buffers[118] = length 0, hash 1 + buffers[119] = length 0, hash 1 + buffers[120] = length 0, hash 1 + buffers[121] = length 0, hash 1 + buffers[122] = length 0, hash 1 + buffers[123] = length 0, hash 1 + buffers[124] = length 0, hash 1 + buffers[125] = length 0, hash 1 + buffers[126] = length 0, hash 1 + buffers[127] = length 0, hash 1 + buffers[128] = length 0, hash 1 + buffers[129] = length 0, hash 1 + buffers[130] = length 0, hash 1 + buffers[131] = length 0, hash 1 + buffers[132] = length 0, hash 1 + buffers[133] = length 0, hash 1 + buffers[134] = length 0, hash 1 + buffers[135] = length 0, hash 1 + buffers[136] = length 0, hash 1 + buffers[137] = length 0, hash 1 + buffers[138] = length 0, hash 1 + buffers[139] = length 0, hash 1 + buffers[140] = length 0, hash 1 + buffers[141] = length 0, hash 1 + buffers[142] = length 0, hash 1 + buffers[143] = length 0, hash 1 + buffers[144] = length 0, hash 1 + buffers[145] = length 0, hash 1 + buffers[146] = length 0, hash 1 + buffers[147] = length 0, hash 1 + buffers[148] = length 0, hash 1 + buffers[149] = length 0, hash 1 + buffers[150] = length 0, hash 1 + buffers[151] = length 0, hash 1 + buffers[152] = length 0, hash 1 + buffers[153] = length 0, hash 1 + buffers[154] = length 0, hash 1 + buffers[155] = length 0, hash 1 + buffers[156] = length 0, hash 1 + buffers[157] = length 0, hash 1 + buffers[158] = length 0, hash 1 + buffers[159] = length 0, hash 1 + buffers[160] = length 0, hash 1 + buffers[161] = length 0, hash 1 + buffers[162] = length 0, hash 1 + buffers[163] = length 0, hash 1 + buffers[164] = length 0, hash 1 + buffers[165] = length 0, hash 1 + buffers[166] = length 0, hash 1 + buffers[167] = length 0, hash 1 + buffers[168] = length 0, hash 1 + buffers[169] = length 0, hash 1 + buffers[170] = length 0, hash 1 + buffers[171] = length 0, hash 1 + buffers[172] = length 0, hash 1 + buffers[173] = length 0, hash 1 + buffers[174] = length 0, hash 1 + buffers[175] = length 0, hash 1 + buffers[176] = length 0, hash 1 + buffers[177] = length 0, hash 1 + buffers[178] = length 0, hash 1 + buffers[179] = length 0, hash 1 + buffers[180] = length 0, hash 1 + buffers[181] = length 0, hash 1 + buffers[182] = length 0, hash 1 + buffers[183] = length 0, hash 1 + buffers[184] = length 0, hash 1 + buffers[185] = length 0, hash 1 + buffers[186] = length 0, hash 1 + buffers[187] = length 0, hash 1 + buffers[188] = length 0, hash 1 + buffers[189] = length 0, hash 1 + buffers[190] = length 0, hash 1 + buffers[191] = length 0, hash 1 + buffers[192] = length 0, hash 1 + buffers[193] = length 0, hash 1 + buffers[194] = length 0, hash 1 + buffers[195] = length 0, hash 1 + buffers[196] = length 0, hash 1 + buffers[197] = length 0, hash 1 + buffers[198] = length 0, hash 1 + buffers[199] = length 0, hash 1 + buffers[200] = length 0, hash 1 + buffers[201] = length 0, hash 1 + buffers[202] = length 0, hash 1 + buffers[203] = length 0, hash 1 + buffers[204] = length 0, hash 1 + buffers[205] = length 0, hash 1 + buffers[206] = length 0, hash 1 + buffers[207] = length 0, hash 1 + buffers[208] = length 0, hash 1 + buffers[209] = length 0, hash 1 + buffers[210] = length 0, hash 1 + buffers[211] = length 0, hash 1 + buffers[212] = length 0, hash 1 + buffers[213] = length 0, hash 1 + buffers[214] = length 0, hash 1 + buffers[215] = length 0, hash 1 + buffers[216] = length 0, hash 1 + buffers[217] = length 0, hash 1 + buffers[218] = length 0, hash 1 + buffers[219] = length 0, hash 1 + buffers[220] = length 0, hash 1 + buffers[221] = length 0, hash 1 + buffers[222] = length 0, hash 1 + buffers[223] = length 0, hash 1 + buffers[224] = length 0, hash 1 + buffers[225] = length 0, hash 1 + buffers[226] = length 0, hash 1 + buffers[227] = length 0, hash 1 + buffers[228] = length 0, hash 1 + buffers[229] = length 0, hash 1 + buffers[230] = length 0, hash 1 + buffers[231] = length 0, hash 1 + buffers[232] = length 0, hash 1 + buffers[233] = length 0, hash 1 + buffers[234] = length 0, hash 1 + buffers[235] = length 0, hash 1 + buffers[236] = length 0, hash 1 + buffers[237] = length 0, hash 1 + buffers[238] = length 0, hash 1 + buffers[239] = length 0, hash 1 + buffers[240] = length 0, hash 1 + buffers[241] = length 0, hash 1 + buffers[242] = length 0, hash 1 + buffers[243] = length 0, hash 1 + buffers[244] = length 0, hash 1 + buffers[245] = length 0, hash 1 + buffers[246] = length 0, hash 1 + buffers[247] = length 0, hash 1 + buffers[248] = length 0, hash 1 + buffers[249] = length 0, hash 1 + buffers[250] = length 0, hash 1 + buffers[251] = length 0, hash 1 + buffers[252] = length 0, hash 1 + buffers[253] = length 0, hash 1 + buffers[254] = length 0, hash 1 + buffers[255] = length 0, hash 1 + buffers[256] = length 0, hash 1 + buffers[257] = length 0, hash 1 + buffers[258] = length 0, hash 1 + buffers[259] = length 0, hash 1 + buffers[260] = length 0, hash 1 + buffers[261] = length 0, hash 1 + buffers[262] = length 0, hash 1 + buffers[263] = length 0, hash 1 + buffers[264] = length 0, hash 1 + buffers[265] = length 0, hash 1 + buffers[266] = length 0, hash 1 + buffers[267] = length 0, hash 1 + buffers[268] = length 0, hash 1 + buffers[269] = length 0, hash 1 + buffers[270] = length 0, hash 1 + buffers[271] = length 0, hash 1 + buffers[272] = length 0, hash 1 + buffers[273] = length 0, hash 1 + buffers[274] = length 0, hash 1 diff --git a/libraries/test_data/src/test/assets/playbackdumps/offloadRecovery/bear.opus.offloadWriteFailureRecovery.dump b/libraries/test_data/src/test/assets/playbackdumps/offloadRecovery/bear.opus.offloadWriteFailureRecovery.dump new file mode 100644 index 0000000000..e8e91b6d3b --- /dev/null +++ b/libraries/test_data/src/test/assets/playbackdumps/offloadRecovery/bear.opus.offloadWriteFailureRecovery.dump @@ -0,0 +1,279 @@ +SinkDump (OggOpus): + buffers.length = 277 + buffers[0] = length 4137, hash 9776A1C3 + buffers[1] = length 3848, hash B3105060 + buffers[2] = length 0, hash 1 + buffers[3] = length 0, hash 1 + buffers[4] = length 0, hash 1 + buffers[5] = length 0, hash 1 + buffers[6] = length 0, hash 1 + buffers[7] = length 0, hash 1 + buffers[8] = length 0, hash 1 + buffers[9] = length 0, hash 1 + buffers[10] = length 0, hash 1 + buffers[11] = length 0, hash 1 + buffers[12] = length 0, hash 1 + buffers[13] = length 0, hash 1 + buffers[14] = length 0, hash 1 + buffers[15] = length 0, hash 1 + buffers[16] = length 0, hash 1 + buffers[17] = length 0, hash 1 + buffers[18] = length 0, hash 1 + buffers[19] = length 0, hash 1 + buffers[20] = length 0, hash 1 + buffers[21] = length 0, hash 1 + buffers[22] = length 0, hash 1 + buffers[23] = length 0, hash 1 + buffers[24] = length 0, hash 1 + buffers[25] = length 0, hash 1 + buffers[26] = length 0, hash 1 + buffers[27] = length 0, hash 1 + buffers[28] = length 0, hash 1 + buffers[29] = length 0, hash 1 + buffers[30] = length 0, hash 1 + buffers[31] = length 0, hash 1 + buffers[32] = length 0, hash 1 + buffers[33] = length 0, hash 1 + buffers[34] = length 0, hash 1 + buffers[35] = length 0, hash 1 + buffers[36] = length 0, hash 1 + buffers[37] = length 0, hash 1 + buffers[38] = length 0, hash 1 + buffers[39] = length 0, hash 1 + buffers[40] = length 0, hash 1 + buffers[41] = length 0, hash 1 + buffers[42] = length 0, hash 1 + buffers[43] = length 0, hash 1 + buffers[44] = length 0, hash 1 + buffers[45] = length 0, hash 1 + buffers[46] = length 0, hash 1 + buffers[47] = length 0, hash 1 + buffers[48] = length 0, hash 1 + buffers[49] = length 0, hash 1 + buffers[50] = length 0, hash 1 + buffers[51] = length 0, hash 1 + buffers[52] = length 0, hash 1 + buffers[53] = length 0, hash 1 + buffers[54] = length 0, hash 1 + buffers[55] = length 0, hash 1 + buffers[56] = length 0, hash 1 + buffers[57] = length 0, hash 1 + buffers[58] = length 0, hash 1 + buffers[59] = length 0, hash 1 + buffers[60] = length 0, hash 1 + buffers[61] = length 0, hash 1 + buffers[62] = length 0, hash 1 + buffers[63] = length 0, hash 1 + buffers[64] = length 0, hash 1 + buffers[65] = length 0, hash 1 + buffers[66] = length 0, hash 1 + buffers[67] = length 0, hash 1 + buffers[68] = length 0, hash 1 + buffers[69] = length 0, hash 1 + buffers[70] = length 0, hash 1 + buffers[71] = length 0, hash 1 + buffers[72] = length 0, hash 1 + buffers[73] = length 0, hash 1 + buffers[74] = length 0, hash 1 + buffers[75] = length 0, hash 1 + buffers[76] = length 0, hash 1 + buffers[77] = length 0, hash 1 + buffers[78] = length 0, hash 1 + buffers[79] = length 0, hash 1 + buffers[80] = length 0, hash 1 + buffers[81] = length 0, hash 1 + buffers[82] = length 0, hash 1 + buffers[83] = length 0, hash 1 + buffers[84] = length 0, hash 1 + buffers[85] = length 0, hash 1 + buffers[86] = length 0, hash 1 + buffers[87] = length 0, hash 1 + buffers[88] = length 0, hash 1 + buffers[89] = length 0, hash 1 + buffers[90] = length 0, hash 1 + buffers[91] = length 0, hash 1 + buffers[92] = length 0, hash 1 + buffers[93] = length 0, hash 1 + buffers[94] = length 0, hash 1 + buffers[95] = length 0, hash 1 + buffers[96] = length 0, hash 1 + buffers[97] = length 0, hash 1 + buffers[98] = length 0, hash 1 + buffers[99] = length 0, hash 1 + buffers[100] = length 0, hash 1 + buffers[101] = length 0, hash 1 + buffers[102] = length 0, hash 1 + buffers[103] = length 0, hash 1 + buffers[104] = length 0, hash 1 + buffers[105] = length 0, hash 1 + buffers[106] = length 0, hash 1 + buffers[107] = length 0, hash 1 + buffers[108] = length 0, hash 1 + buffers[109] = length 0, hash 1 + buffers[110] = length 0, hash 1 + buffers[111] = length 0, hash 1 + buffers[112] = length 0, hash 1 + buffers[113] = length 0, hash 1 + buffers[114] = length 0, hash 1 + buffers[115] = length 0, hash 1 + buffers[116] = length 0, hash 1 + buffers[117] = length 0, hash 1 + buffers[118] = length 0, hash 1 + buffers[119] = length 0, hash 1 + buffers[120] = length 0, hash 1 + buffers[121] = length 0, hash 1 + buffers[122] = length 0, hash 1 + buffers[123] = length 0, hash 1 + buffers[124] = length 0, hash 1 + buffers[125] = length 0, hash 1 + buffers[126] = length 0, hash 1 + buffers[127] = length 0, hash 1 + buffers[128] = length 0, hash 1 + buffers[129] = length 0, hash 1 + buffers[130] = length 0, hash 1 + buffers[131] = length 0, hash 1 + buffers[132] = length 0, hash 1 + buffers[133] = length 0, hash 1 + buffers[134] = length 0, hash 1 + buffers[135] = length 0, hash 1 + buffers[136] = length 0, hash 1 + buffers[137] = length 0, hash 1 + buffers[138] = length 0, hash 1 + buffers[139] = length 0, hash 1 + buffers[140] = length 0, hash 1 + buffers[141] = length 0, hash 1 + buffers[142] = length 0, hash 1 + buffers[143] = length 0, hash 1 + buffers[144] = length 0, hash 1 + buffers[145] = length 0, hash 1 + buffers[146] = length 0, hash 1 + buffers[147] = length 0, hash 1 + buffers[148] = length 0, hash 1 + buffers[149] = length 0, hash 1 + buffers[150] = length 0, hash 1 + buffers[151] = length 0, hash 1 + buffers[152] = length 0, hash 1 + buffers[153] = length 0, hash 1 + buffers[154] = length 0, hash 1 + buffers[155] = length 0, hash 1 + buffers[156] = length 0, hash 1 + buffers[157] = length 0, hash 1 + buffers[158] = length 0, hash 1 + buffers[159] = length 0, hash 1 + buffers[160] = length 0, hash 1 + buffers[161] = length 0, hash 1 + buffers[162] = length 0, hash 1 + buffers[163] = length 0, hash 1 + buffers[164] = length 0, hash 1 + buffers[165] = length 0, hash 1 + buffers[166] = length 0, hash 1 + buffers[167] = length 0, hash 1 + buffers[168] = length 0, hash 1 + buffers[169] = length 0, hash 1 + buffers[170] = length 0, hash 1 + buffers[171] = length 0, hash 1 + buffers[172] = length 0, hash 1 + buffers[173] = length 0, hash 1 + buffers[174] = length 0, hash 1 + buffers[175] = length 0, hash 1 + buffers[176] = length 0, hash 1 + buffers[177] = length 0, hash 1 + buffers[178] = length 0, hash 1 + buffers[179] = length 0, hash 1 + buffers[180] = length 0, hash 1 + buffers[181] = length 0, hash 1 + buffers[182] = length 0, hash 1 + buffers[183] = length 0, hash 1 + buffers[184] = length 0, hash 1 + buffers[185] = length 0, hash 1 + buffers[186] = length 0, hash 1 + buffers[187] = length 0, hash 1 + buffers[188] = length 0, hash 1 + buffers[189] = length 0, hash 1 + buffers[190] = length 0, hash 1 + buffers[191] = length 0, hash 1 + buffers[192] = length 0, hash 1 + buffers[193] = length 0, hash 1 + buffers[194] = length 0, hash 1 + buffers[195] = length 0, hash 1 + buffers[196] = length 0, hash 1 + buffers[197] = length 0, hash 1 + buffers[198] = length 0, hash 1 + buffers[199] = length 0, hash 1 + buffers[200] = length 0, hash 1 + buffers[201] = length 0, hash 1 + buffers[202] = length 0, hash 1 + buffers[203] = length 0, hash 1 + buffers[204] = length 0, hash 1 + buffers[205] = length 0, hash 1 + buffers[206] = length 0, hash 1 + buffers[207] = length 0, hash 1 + buffers[208] = length 0, hash 1 + buffers[209] = length 0, hash 1 + buffers[210] = length 0, hash 1 + buffers[211] = length 0, hash 1 + buffers[212] = length 0, hash 1 + buffers[213] = length 0, hash 1 + buffers[214] = length 0, hash 1 + buffers[215] = length 0, hash 1 + buffers[216] = length 0, hash 1 + buffers[217] = length 0, hash 1 + buffers[218] = length 0, hash 1 + buffers[219] = length 0, hash 1 + buffers[220] = length 0, hash 1 + buffers[221] = length 0, hash 1 + buffers[222] = length 0, hash 1 + buffers[223] = length 0, hash 1 + buffers[224] = length 0, hash 1 + buffers[225] = length 0, hash 1 + buffers[226] = length 0, hash 1 + buffers[227] = length 0, hash 1 + buffers[228] = length 0, hash 1 + buffers[229] = length 0, hash 1 + buffers[230] = length 0, hash 1 + buffers[231] = length 0, hash 1 + buffers[232] = length 0, hash 1 + buffers[233] = length 0, hash 1 + buffers[234] = length 0, hash 1 + buffers[235] = length 0, hash 1 + buffers[236] = length 0, hash 1 + buffers[237] = length 0, hash 1 + buffers[238] = length 0, hash 1 + buffers[239] = length 0, hash 1 + buffers[240] = length 0, hash 1 + buffers[241] = length 0, hash 1 + buffers[242] = length 0, hash 1 + buffers[243] = length 0, hash 1 + buffers[244] = length 0, hash 1 + buffers[245] = length 0, hash 1 + buffers[246] = length 0, hash 1 + buffers[247] = length 0, hash 1 + buffers[248] = length 0, hash 1 + buffers[249] = length 0, hash 1 + buffers[250] = length 0, hash 1 + buffers[251] = length 0, hash 1 + buffers[252] = length 0, hash 1 + buffers[253] = length 0, hash 1 + buffers[254] = length 0, hash 1 + buffers[255] = length 0, hash 1 + buffers[256] = length 0, hash 1 + buffers[257] = length 0, hash 1 + buffers[258] = length 0, hash 1 + buffers[259] = length 0, hash 1 + buffers[260] = length 0, hash 1 + buffers[261] = length 0, hash 1 + buffers[262] = length 0, hash 1 + buffers[263] = length 0, hash 1 + buffers[264] = length 0, hash 1 + buffers[265] = length 0, hash 1 + buffers[266] = length 0, hash 1 + buffers[267] = length 0, hash 1 + buffers[268] = length 0, hash 1 + buffers[269] = length 0, hash 1 + buffers[270] = length 0, hash 1 + buffers[271] = length 0, hash 1 + buffers[272] = length 0, hash 1 + buffers[273] = length 0, hash 1 + buffers[274] = length 0, hash 1 + buffers[275] = length 0, hash 1 + buffers[276] = length 0, hash 1