diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
index b01ff7345f..4ec40f6846 100644
--- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
+++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
@@ -113,6 +113,8 @@ public final class CastPlayer extends BasePlayer {
private static final long PROGRESS_REPORT_PERIOD_MS = 1000;
private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0];
+ private static final CueGroup EMPTY_CUE_GROUP =
+ new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
private final CastContext castContext;
private final MediaItemConverter mediaItemConverter;
@@ -714,7 +716,7 @@ public final class CastPlayer extends BasePlayer {
/** This method is not supported and returns an empty {@link CueGroup}. */
@Override
public CueGroup getCurrentCues() {
- return CueGroup.EMPTY;
+ return EMPTY_CUE_GROUP;
}
/** This method is not supported and always returns {@link DeviceInfo#UNKNOWN}. */
diff --git a/libraries/common/src/main/java/androidx/media3/common/text/CueGroup.java b/libraries/common/src/main/java/androidx/media3/common/text/CueGroup.java
index 9a26d0ec26..3d1f1bec20 100644
--- a/libraries/common/src/main/java/androidx/media3/common/text/CueGroup.java
+++ b/libraries/common/src/main/java/androidx/media3/common/text/CueGroup.java
@@ -22,6 +22,7 @@ import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.Bundleable;
+import androidx.media3.common.Timeline;
import androidx.media3.common.util.BundleableUtil;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
@@ -34,10 +35,6 @@ import java.util.List;
/** Class to represent the state of active {@link Cue Cues} at a particular time. */
public final class CueGroup implements Bundleable {
-
- /** Empty {@link CueGroup}. */
- @UnstableApi public static final CueGroup EMPTY = new CueGroup(ImmutableList.of());
-
/**
* The cues in this group.
*
@@ -47,11 +44,18 @@ public final class CueGroup implements Bundleable {
*
This list may be empty if the group represents a state with no cues.
*/
public final ImmutableList cues;
+ /**
+ * The presentation time of the {@link #cues}, in microseconds.
+ *
+ * This time is an offset from the start of the current {@link Timeline.Period}
+ */
+ @UnstableApi public final long presentationTimeUs;
/** Creates a CueGroup. */
@UnstableApi
- public CueGroup(List cues) {
+ public CueGroup(List cues, long presentationTimeUs) {
this.cues = ImmutableList.copyOf(cues);
+ this.presentationTimeUs = presentationTimeUs;
}
// Bundleable implementation.
@@ -59,10 +63,11 @@ public final class CueGroup implements Bundleable {
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
- @IntDef({FIELD_CUES})
+ @IntDef({FIELD_CUES, FIELD_PRESENTATION_TIME_US})
private @interface FieldNumber {}
private static final int FIELD_CUES = 0;
+ private static final int FIELD_PRESENTATION_TIME_US = 1;
@UnstableApi
@Override
@@ -70,6 +75,7 @@ public final class CueGroup implements Bundleable {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
keyForField(FIELD_CUES), BundleableUtil.toBundleArrayList(filterOutBitmapCues(cues)));
+ bundle.putLong(keyForField(FIELD_PRESENTATION_TIME_US), presentationTimeUs);
return bundle;
}
@@ -81,7 +87,8 @@ public final class CueGroup implements Bundleable {
cueBundles == null
? ImmutableList.of()
: BundleableUtil.fromBundleList(Cue.CREATOR, cueBundles);
- return new CueGroup(cues);
+ long presentationTimeUs = bundle.getLong(keyForField(FIELD_PRESENTATION_TIME_US));
+ return new CueGroup(cues, presentationTimeUs);
}
private static String keyForField(@FieldNumber int field) {
diff --git a/libraries/common/src/test/java/androidx/media3/common/text/CueGroupTest.java b/libraries/common/src/test/java/androidx/media3/common/text/CueGroupTest.java
index 6a8f4d6e9c..03d898f3fa 100644
--- a/libraries/common/src/test/java/androidx/media3/common/text/CueGroupTest.java
+++ b/libraries/common/src/test/java/androidx/media3/common/text/CueGroupTest.java
@@ -37,7 +37,7 @@ public class CueGroupTest {
Cue bitmapCue =
new Cue.Builder().setBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)).build();
ImmutableList cues = ImmutableList.of(textCue, bitmapCue);
- CueGroup cueGroup = new CueGroup(cues);
+ CueGroup cueGroup = new CueGroup(cues, /* presentationTimeUs= */ 1_230_000);
Parcel parcel = Parcel.obtain();
try {
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
index 389112484a..a8587da565 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
@@ -355,7 +355,7 @@ import java.util.concurrent.TimeoutException;
} else {
audioSessionId = Util.generateAudioSessionIdV21(applicationContext);
}
- currentCueGroup = CueGroup.EMPTY;
+ currentCueGroup = new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
throwsWhenUsingWrongThread = true;
addListener(analyticsCollector);
@@ -939,7 +939,7 @@ import java.util.concurrent.TimeoutException;
verifyApplicationThread();
audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE);
stopInternal(reset, /* error= */ null);
- currentCueGroup = CueGroup.EMPTY;
+ currentCueGroup = new CueGroup(ImmutableList.of(), playbackInfo.positionUs);
}
@Override
@@ -993,7 +993,7 @@ import java.util.concurrent.TimeoutException;
checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK);
isPriorityTaskManagerRegistered = false;
}
- currentCueGroup = CueGroup.EMPTY;
+ currentCueGroup = new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
playerReleased = true;
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java
index 506a69a842..2ddbd5908b 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java
@@ -42,12 +42,13 @@ import androidx.media3.extractor.text.SubtitleDecoder;
import androidx.media3.extractor.text.SubtitleDecoderException;
import androidx.media3.extractor.text.SubtitleInputBuffer;
import androidx.media3.extractor.text.SubtitleOutputBuffer;
+import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.util.Collections;
-import java.util.List;
+import org.checkerframework.checker.nullness.qual.RequiresNonNull;
+import org.checkerframework.dataflow.qual.SideEffectFree;
/**
* A renderer for text.
@@ -103,6 +104,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Nullable private SubtitleOutputBuffer nextSubtitle;
private int nextSubtitleEventIndex;
private long finalStreamEndPositionUs;
+ private long outputStreamOffsetUs;
+ private long lastRendererPositionUs;
/**
* @param output The output.
@@ -134,6 +137,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
this.decoderFactory = decoderFactory;
formatHolder = new FormatHolder();
finalStreamEndPositionUs = C.TIME_UNSET;
+ outputStreamOffsetUs = C.TIME_UNSET;
+ lastRendererPositionUs = C.TIME_UNSET;
}
@Override
@@ -170,6 +175,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
+ outputStreamOffsetUs = offsetUs;
streamFormat = formats[0];
if (decoder != null) {
decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM;
@@ -180,6 +186,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override
protected void onPositionReset(long positionUs, boolean joining) {
+ lastRendererPositionUs = positionUs;
clearOutput();
inputStreamEnded = false;
outputStreamEnded = false;
@@ -194,6 +201,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override
public void render(long positionUs, long elapsedRealtimeUs) {
+ lastRendererPositionUs = positionUs;
if (isCurrentStreamFinal()
&& finalStreamEndPositionUs != C.TIME_UNSET
&& positionUs >= finalStreamEndPositionUs) {
@@ -257,7 +265,9 @@ public final class TextRenderer extends BaseRenderer implements Callback {
// If textRendererNeedsUpdate then subtitle must be non-null.
checkNotNull(subtitle);
// textRendererNeedsUpdate is set and we're playing. Update the renderer.
- updateOutput(subtitle.getCues(positionUs));
+ long presentationTimeUs = getPresentationTimeUs(getCurrentEventTimeUs(positionUs));
+ CueGroup cueGroup = new CueGroup(subtitle.getCues(positionUs), presentationTimeUs);
+ updateOutput(cueGroup);
}
if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) {
@@ -315,6 +325,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
streamFormat = null;
finalStreamEndPositionUs = C.TIME_UNSET;
clearOutput();
+ outputStreamOffsetUs = C.TIME_UNSET;
+ lastRendererPositionUs = C.TIME_UNSET;
releaseDecoder();
}
@@ -370,33 +382,33 @@ public final class TextRenderer extends BaseRenderer implements Callback {
: subtitle.getEventTime(nextSubtitleEventIndex);
}
- private void updateOutput(List cues) {
+ private void updateOutput(CueGroup cueGroup) {
if (outputHandler != null) {
- outputHandler.obtainMessage(MSG_UPDATE_OUTPUT, cues).sendToTarget();
+ outputHandler.obtainMessage(MSG_UPDATE_OUTPUT, cueGroup).sendToTarget();
} else {
- invokeUpdateOutputInternal(cues);
+ invokeUpdateOutputInternal(cueGroup);
}
}
private void clearOutput() {
- updateOutput(Collections.emptyList());
+ updateOutput(new CueGroup(ImmutableList.of(), getPresentationTimeUs(lastRendererPositionUs)));
}
- @SuppressWarnings("unchecked")
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_OUTPUT:
- invokeUpdateOutputInternal((List) msg.obj);
+ invokeUpdateOutputInternal((CueGroup) msg.obj);
return true;
default:
throw new IllegalStateException();
}
}
- private void invokeUpdateOutputInternal(List cues) {
- output.onCues(cues);
- output.onCues(new CueGroup(cues));
+ @SuppressWarnings("deprecation") // We need to call both onCues method for backward compatibility.
+ private void invokeUpdateOutputInternal(CueGroup cueGroup) {
+ output.onCues(cueGroup.cues);
+ output.onCues(cueGroup);
}
/**
@@ -410,4 +422,25 @@ public final class TextRenderer extends BaseRenderer implements Callback {
clearOutput();
replaceDecoder();
}
+
+ @RequiresNonNull("subtitle")
+ @SideEffectFree
+ private long getCurrentEventTimeUs(long positionUs) {
+ int nextEventTimeIndex = subtitle.getNextEventTimeIndex(positionUs);
+ if (nextEventTimeIndex == 0) {
+ return subtitle.timeUs;
+ }
+
+ return nextEventTimeIndex == C.INDEX_UNSET
+ ? subtitle.getEventTime(subtitle.getEventTimeCount() - 1)
+ : subtitle.getEventTime(nextEventTimeIndex - 1);
+ }
+
+ @SideEffectFree
+ private long getPresentationTimeUs(long positionUs) {
+ checkState(positionUs != C.TIME_UNSET);
+ checkState(outputStreamOffsetUs != C.TIME_UNSET);
+
+ return positionUs - outputStreamOffsetUs;
+ }
}
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java
index 1987a556dc..d4de66ad9e 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java
@@ -16,9 +16,16 @@
package androidx.media3.exoplayer.e2etest;
import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.net.Uri;
+import android.view.Surface;
+import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
+import androidx.media3.common.MimeTypes;
import androidx.media3.common.Player;
import androidx.media3.exoplayer.ExoPlayer;
+import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
+import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.test.utils.CapturingRenderersFactory;
import androidx.media3.test.utils.DumpFileAsserts;
import androidx.media3.test.utils.FakeClock;
@@ -27,6 +34,7 @@ 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 com.google.common.collect.ImmutableList;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,4 +90,43 @@ public final class PlaylistPlaybackTest {
DumpFileAsserts.assertOutput(
applicationContext, playbackOutput, "playbackdumps/playlists/bypass-off-then-on.dump");
}
+
+ @Test
+ public void test_subtitle() throws Exception {
+ Context applicationContext = ApplicationProvider.getApplicationContext();
+ CapturingRenderersFactory capturingRenderersFactory =
+ new CapturingRenderersFactory(applicationContext);
+ MediaSource.Factory mediaSourceFactory =
+ new DefaultMediaSourceFactory(applicationContext)
+ .experimentalUseProgressiveMediaSourceForSubtitles(true);
+ ExoPlayer player =
+ new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
+ .setClock(new FakeClock(/* isAutoAdvancing= */ true))
+ .setMediaSourceFactory(mediaSourceFactory)
+ .build();
+ player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
+ PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
+
+ player.addMediaItem(MediaItem.fromUri("asset:///media/mp4/preroll-5s.mp4"));
+ MediaItem mediaItemWithSubtitle =
+ new MediaItem.Builder()
+ .setUri("asset:///media/mp4/preroll-5s.mp4")
+ .setSubtitleConfigurations(
+ ImmutableList.of(
+ new MediaItem.SubtitleConfiguration.Builder(
+ Uri.parse("asset:///media/webvtt/typical"))
+ .setMimeType(MimeTypes.TEXT_VTT)
+ .setLanguage("en")
+ .setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
+ .build()))
+ .build();
+ player.addMediaItem(mediaItemWithSubtitle);
+ player.prepare();
+ player.play();
+ TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
+ player.release();
+
+ DumpFileAsserts.assertOutput(
+ applicationContext, playbackOutput, "playbackdumps/playlists/playlist_with_subtitles.dump");
+ }
}
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaController.java b/libraries/session/src/main/java/androidx/media3/session/MediaController.java
index e47fc02253..a3a140c772 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaController.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaController.java
@@ -56,6 +56,7 @@ import androidx.media3.common.util.Consumer;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
+import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
@@ -164,6 +165,9 @@ public class MediaController implements Player {
"MediaController method is called from a wrong thread."
+ " See javadoc of MediaController for details.";
+ private static final CueGroup EMPTY_CUE_GROUP =
+ new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
+
/** A builder for {@link MediaController}. */
public static final class Builder {
@@ -1576,7 +1580,7 @@ public class MediaController implements Player {
@Override
public CueGroup getCurrentCues() {
verifyApplicationThread();
- return isConnected() ? impl.getCurrentCues() : CueGroup.EMPTY;
+ return isConnected() ? impl.getCurrentCues() : EMPTY_CUE_GROUP;
}
@Override
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
index 6a2dc56c1f..04006c7e53 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
@@ -105,6 +105,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
private static final long AGGREGATES_CALLBACKS_WITHIN_TIMEOUT_MS = 500L;
private static final int VOLUME_FLAGS = AudioManager.FLAG_SHOW_UI;
+ private static final CueGroup EMPTY_CUE_GROUP =
+ new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
/* package */ final Context context;
/* package */ final MediaController instance;
@@ -1016,7 +1018,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
@Override
public CueGroup getCurrentCues() {
Log.w(TAG, "Session doesn't support getting Cue");
- return CueGroup.EMPTY;
+ return EMPTY_CUE_GROUP;
}
@Override
@@ -2086,7 +2088,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
/* playlistMetadata= */ playlistMetadata,
/* volume= */ 1.0f,
/* audioAttributes= */ audioAttributes,
- /* cueGroup= */ CueGroup.EMPTY,
+ /* cueGroup= */ EMPTY_CUE_GROUP,
/* deviceInfo= */ deviceInfo,
/* deviceVolume= */ deviceVolume,
/* deviceMuted= */ deviceMuted,
diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java
index f6bd408b7a..6b886bb568 100644
--- a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java
+++ b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java
@@ -45,6 +45,7 @@ import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.VideoSize;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Assertions;
+import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -56,6 +57,9 @@ import java.lang.annotation.Target;
*/
/* package */ class PlayerInfo implements Bundleable {
+ private static final CueGroup EMPTY_CUE_GROUP =
+ new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
+
public static class Builder {
@Nullable private PlaybackException playerError;
@@ -332,7 +336,7 @@ import java.lang.annotation.Target;
MediaMetadata.EMPTY,
/* volume= */ 1f,
AudioAttributes.DEFAULT,
- /* cueGroup = */ CueGroup.EMPTY,
+ /* cueGroup = */ EMPTY_CUE_GROUP,
DeviceInfo.UNKNOWN,
/* deviceVolume= */ 0,
/* deviceMuted= */ false,
@@ -846,7 +850,7 @@ import java.lang.annotation.Target;
: AudioAttributes.CREATOR.fromBundle(audioAttributesBundle);
@Nullable Bundle cueGroupBundle = bundle.getBundle(keyForField(FIELD_CUE_GROUP));
CueGroup cueGroup =
- cueGroupBundle == null ? CueGroup.EMPTY : CueGroup.CREATOR.fromBundle(cueGroupBundle);
+ cueGroupBundle == null ? EMPTY_CUE_GROUP : CueGroup.CREATOR.fromBundle(cueGroupBundle);
@Nullable Bundle deviceInfoBundle = bundle.getBundle(keyForField(FIELD_DEVICE_INFO));
DeviceInfo deviceInfo =
deviceInfoBundle == null
diff --git a/libraries/test_data/src/test/assets/playbackdumps/playlists/playlist_with_subtitles.dump b/libraries/test_data/src/test/assets/playbackdumps/playlists/playlist_with_subtitles.dump
new file mode 100644
index 0000000000..718fd4bd43
--- /dev/null
+++ b/libraries/test_data/src/test/assets/playbackdumps/playlists/playlist_with_subtitles.dump
@@ -0,0 +1,723 @@
+MediaCodecAdapter (exotest.audio.aac):
+ buffers.length = 436
+ buffers[0] = length 21, hash D57A2CCC
+ buffers[1] = length 4, hash EE9DF
+ buffers[2] = length 4, hash EE9DF
+ buffers[3] = length 4, hash EE9DF
+ buffers[4] = length 4, hash EE9DF
+ buffers[5] = length 4, hash EE9DF
+ buffers[6] = length 4, hash EE9DF
+ buffers[7] = length 4, hash EE9DF
+ buffers[8] = length 4, hash EE9DF
+ buffers[9] = length 4, hash EE9DF
+ buffers[10] = length 4, hash EE9DF
+ buffers[11] = length 4, hash EE9DF
+ buffers[12] = length 4, hash EE9DF
+ buffers[13] = length 4, hash EE9DF
+ buffers[14] = length 4, hash EE9DF
+ buffers[15] = length 4, hash EE9DF
+ buffers[16] = length 4, hash EE9DF
+ buffers[17] = length 4, hash EE9DF
+ buffers[18] = length 4, hash EE9DF
+ buffers[19] = length 4, hash EE9DF
+ buffers[20] = length 4, hash EE9DF
+ buffers[21] = length 4, hash EE9DF
+ buffers[22] = length 4, hash EE9DF
+ buffers[23] = length 4, hash EE9DF
+ buffers[24] = length 4, hash EE9DF
+ buffers[25] = length 4, hash EE9DF
+ buffers[26] = length 4, hash EE9DF
+ buffers[27] = length 4, hash EE9DF
+ buffers[28] = length 4, hash EE9DF
+ buffers[29] = length 4, hash EE9DF
+ buffers[30] = length 4, hash EE9DF
+ buffers[31] = length 4, hash EE9DF
+ buffers[32] = length 4, hash EE9DF
+ buffers[33] = length 4, hash EE9DF
+ buffers[34] = length 4, hash EE9DF
+ buffers[35] = length 4, hash EE9DF
+ buffers[36] = length 4, hash EE9DF
+ buffers[37] = length 4, hash EE9DF
+ buffers[38] = length 4, hash EE9DF
+ buffers[39] = length 4, hash EE9DF
+ buffers[40] = length 4, hash EE9DF
+ buffers[41] = length 4, hash EE9DF
+ buffers[42] = length 4, hash EE9DF
+ buffers[43] = length 4, hash EE9DF
+ buffers[44] = length 4, hash EE9DF
+ buffers[45] = length 4, hash EE9DF
+ buffers[46] = length 4, hash EE9DF
+ buffers[47] = length 4, hash EE9DF
+ buffers[48] = length 4, hash EE9DF
+ buffers[49] = length 4, hash EE9DF
+ buffers[50] = length 4, hash EE9DF
+ buffers[51] = length 4, hash EE9DF
+ buffers[52] = length 4, hash EE9DF
+ buffers[53] = length 4, hash EE9DF
+ buffers[54] = length 4, hash EE9DF
+ buffers[55] = length 4, hash EE9DF
+ buffers[56] = length 4, hash EE9DF
+ buffers[57] = length 4, hash EE9DF
+ buffers[58] = length 4, hash EE9DF
+ buffers[59] = length 4, hash EE9DF
+ buffers[60] = length 4, hash EE9DF
+ buffers[61] = length 4, hash EE9DF
+ buffers[62] = length 4, hash EE9DF
+ buffers[63] = length 4, hash EE9DF
+ buffers[64] = length 4, hash EE9DF
+ buffers[65] = length 4, hash EE9DF
+ buffers[66] = length 4, hash EE9DF
+ buffers[67] = length 4, hash EE9DF
+ buffers[68] = length 4, hash EE9DF
+ buffers[69] = length 4, hash EE9DF
+ buffers[70] = length 4, hash EE9DF
+ buffers[71] = length 4, hash EE9DF
+ buffers[72] = length 4, hash EE9DF
+ buffers[73] = length 4, hash EE9DF
+ buffers[74] = length 4, hash EE9DF
+ buffers[75] = length 4, hash EE9DF
+ buffers[76] = length 4, hash EE9DF
+ buffers[77] = length 4, hash EE9DF
+ buffers[78] = length 4, hash EE9DF
+ buffers[79] = length 4, hash EE9DF
+ buffers[80] = length 4, hash EE9DF
+ buffers[81] = length 4, hash EE9DF
+ buffers[82] = length 4, hash EE9DF
+ buffers[83] = length 4, hash EE9DF
+ buffers[84] = length 4, hash EE9DF
+ buffers[85] = length 4, hash EE9DF
+ buffers[86] = length 4, hash EE9DF
+ buffers[87] = length 4, hash EE9DF
+ buffers[88] = length 4, hash EE9DF
+ buffers[89] = length 4, hash EE9DF
+ buffers[90] = length 4, hash EE9DF
+ buffers[91] = length 4, hash EE9DF
+ buffers[92] = length 4, hash EE9DF
+ buffers[93] = length 4, hash EE9DF
+ buffers[94] = length 4, hash EE9DF
+ buffers[95] = length 4, hash EE9DF
+ buffers[96] = length 4, hash EE9DF
+ buffers[97] = length 4, hash EE9DF
+ buffers[98] = length 4, hash EE9DF
+ buffers[99] = length 4, hash EE9DF
+ buffers[100] = length 4, hash EE9DF
+ buffers[101] = length 4, hash EE9DF
+ buffers[102] = length 4, hash EE9DF
+ buffers[103] = length 4, hash EE9DF
+ buffers[104] = length 4, hash EE9DF
+ buffers[105] = length 4, hash EE9DF
+ buffers[106] = length 4, hash EE9DF
+ buffers[107] = length 4, hash EE9DF
+ buffers[108] = length 4, hash EE9DF
+ buffers[109] = length 4, hash EE9DF
+ buffers[110] = length 4, hash EE9DF
+ buffers[111] = length 4, hash EE9DF
+ buffers[112] = length 4, hash EE9DF
+ buffers[113] = length 4, hash EE9DF
+ buffers[114] = length 4, hash EE9DF
+ buffers[115] = length 4, hash EE9DF
+ buffers[116] = length 4, hash EE9DF
+ buffers[117] = length 4, hash EE9DF
+ buffers[118] = length 4, hash EE9DF
+ buffers[119] = length 4, hash EE9DF
+ buffers[120] = length 4, hash EE9DF
+ buffers[121] = length 4, hash EE9DF
+ buffers[122] = length 4, hash EE9DF
+ buffers[123] = length 4, hash EE9DF
+ buffers[124] = length 4, hash EE9DF
+ buffers[125] = length 4, hash EE9DF
+ buffers[126] = length 4, hash EE9DF
+ buffers[127] = length 4, hash EE9DF
+ buffers[128] = length 4, hash EE9DF
+ buffers[129] = length 4, hash EE9DF
+ buffers[130] = length 4, hash EE9DF
+ buffers[131] = length 4, hash EE9DF
+ buffers[132] = length 4, hash EE9DF
+ buffers[133] = length 4, hash EE9DF
+ buffers[134] = length 4, hash EE9DF
+ buffers[135] = length 4, hash EE9DF
+ buffers[136] = length 4, hash EE9DF
+ buffers[137] = length 4, hash EE9DF
+ buffers[138] = length 4, hash EE9DF
+ buffers[139] = length 4, hash EE9DF
+ buffers[140] = length 4, hash EE9DF
+ buffers[141] = length 4, hash EE9DF
+ buffers[142] = length 4, hash EE9DF
+ buffers[143] = length 4, hash EE9DF
+ buffers[144] = length 4, hash EE9DF
+ buffers[145] = length 4, hash EE9DF
+ buffers[146] = length 4, hash EE9DF
+ buffers[147] = length 4, hash EE9DF
+ buffers[148] = length 4, hash EE9DF
+ buffers[149] = length 4, hash EE9DF
+ buffers[150] = length 4, hash EE9DF
+ buffers[151] = length 4, hash EE9DF
+ buffers[152] = length 4, hash EE9DF
+ buffers[153] = length 4, hash EE9DF
+ buffers[154] = length 4, hash EE9DF
+ buffers[155] = length 4, hash EE9DF
+ buffers[156] = length 4, hash EE9DF
+ buffers[157] = length 4, hash EE9DF
+ buffers[158] = length 4, hash EE9DF
+ buffers[159] = length 4, hash EE9DF
+ buffers[160] = length 4, hash EE9DF
+ buffers[161] = length 4, hash EE9DF
+ buffers[162] = length 4, hash EE9DF
+ buffers[163] = length 4, hash EE9DF
+ buffers[164] = length 4, hash EE9DF
+ buffers[165] = length 4, hash EE9DF
+ buffers[166] = length 4, hash EE9DF
+ buffers[167] = length 4, hash EE9DF
+ buffers[168] = length 4, hash EE9DF
+ buffers[169] = length 4, hash EE9DF
+ buffers[170] = length 4, hash EE9DF
+ buffers[171] = length 4, hash EE9DF
+ buffers[172] = length 4, hash EE9DF
+ buffers[173] = length 4, hash EE9DF
+ buffers[174] = length 4, hash EE9DF
+ buffers[175] = length 4, hash EE9DF
+ buffers[176] = length 4, hash EE9DF
+ buffers[177] = length 4, hash EE9DF
+ buffers[178] = length 4, hash EE9DF
+ buffers[179] = length 4, hash EE9DF
+ buffers[180] = length 4, hash EE9DF
+ buffers[181] = length 4, hash EE9DF
+ buffers[182] = length 4, hash EE9DF
+ buffers[183] = length 4, hash EE9DF
+ buffers[184] = length 4, hash EE9DF
+ buffers[185] = length 4, hash EE9DF
+ buffers[186] = length 4, hash EE9DF
+ buffers[187] = length 4, hash EE9DF
+ buffers[188] = length 4, hash EE9DF
+ buffers[189] = length 4, hash EE9DF
+ buffers[190] = length 4, hash EE9DF
+ buffers[191] = length 4, hash EE9DF
+ buffers[192] = length 4, hash EE9DF
+ buffers[193] = length 4, hash EE9DF
+ buffers[194] = length 4, hash EE9DF
+ buffers[195] = length 4, hash EE9DF
+ buffers[196] = length 4, hash EE9DF
+ buffers[197] = length 4, hash EE9DF
+ buffers[198] = length 4, hash EE9DF
+ buffers[199] = length 4, hash EE9DF
+ buffers[200] = length 4, hash EE9DF
+ buffers[201] = length 4, hash EE9DF
+ buffers[202] = length 4, hash EE9DF
+ buffers[203] = length 4, hash EE9DF
+ buffers[204] = length 4, hash EE9DF
+ buffers[205] = length 4, hash EE9DF
+ buffers[206] = length 4, hash EE9DF
+ buffers[207] = length 4, hash EE9DF
+ buffers[208] = length 4, hash EE9DF
+ buffers[209] = length 4, hash EE9DF
+ buffers[210] = length 4, hash EE9DF
+ buffers[211] = length 4, hash EE9DF
+ buffers[212] = length 4, hash EE9DF
+ buffers[213] = length 4, hash EE9DF
+ buffers[214] = length 4, hash EE9DF
+ buffers[215] = length 4, hash EE9DF
+ buffers[216] = length 4, hash EE9DF
+ buffers[217] = length 0, hash 1
+ buffers[218] = length 21, hash D57A2CCC
+ buffers[219] = length 4, hash EE9DF
+ buffers[220] = length 4, hash EE9DF
+ buffers[221] = length 4, hash EE9DF
+ buffers[222] = length 4, hash EE9DF
+ buffers[223] = length 4, hash EE9DF
+ buffers[224] = length 4, hash EE9DF
+ buffers[225] = length 4, hash EE9DF
+ buffers[226] = length 4, hash EE9DF
+ buffers[227] = length 4, hash EE9DF
+ buffers[228] = length 4, hash EE9DF
+ buffers[229] = length 4, hash EE9DF
+ buffers[230] = length 4, hash EE9DF
+ buffers[231] = length 4, hash EE9DF
+ buffers[232] = length 4, hash EE9DF
+ buffers[233] = length 4, hash EE9DF
+ buffers[234] = length 4, hash EE9DF
+ buffers[235] = length 4, hash EE9DF
+ buffers[236] = length 4, hash EE9DF
+ buffers[237] = length 4, hash EE9DF
+ buffers[238] = length 4, hash EE9DF
+ buffers[239] = length 4, hash EE9DF
+ buffers[240] = length 4, hash EE9DF
+ buffers[241] = length 4, hash EE9DF
+ buffers[242] = length 4, hash EE9DF
+ buffers[243] = length 4, hash EE9DF
+ buffers[244] = length 4, hash EE9DF
+ buffers[245] = length 4, hash EE9DF
+ buffers[246] = length 4, hash EE9DF
+ buffers[247] = length 4, hash EE9DF
+ buffers[248] = length 4, hash EE9DF
+ buffers[249] = length 4, hash EE9DF
+ buffers[250] = length 4, hash EE9DF
+ buffers[251] = length 4, hash EE9DF
+ buffers[252] = length 4, hash EE9DF
+ buffers[253] = length 4, hash EE9DF
+ buffers[254] = length 4, hash EE9DF
+ buffers[255] = length 4, hash EE9DF
+ buffers[256] = length 4, hash EE9DF
+ buffers[257] = length 4, hash EE9DF
+ buffers[258] = length 4, hash EE9DF
+ buffers[259] = length 4, hash EE9DF
+ buffers[260] = length 4, hash EE9DF
+ buffers[261] = length 4, hash EE9DF
+ buffers[262] = length 4, hash EE9DF
+ buffers[263] = length 4, hash EE9DF
+ buffers[264] = length 4, hash EE9DF
+ buffers[265] = length 4, hash EE9DF
+ buffers[266] = length 4, hash EE9DF
+ buffers[267] = length 4, hash EE9DF
+ buffers[268] = length 4, hash EE9DF
+ buffers[269] = length 4, hash EE9DF
+ buffers[270] = length 4, hash EE9DF
+ buffers[271] = length 4, hash EE9DF
+ buffers[272] = length 4, hash EE9DF
+ buffers[273] = length 4, hash EE9DF
+ buffers[274] = length 4, hash EE9DF
+ buffers[275] = length 4, hash EE9DF
+ buffers[276] = length 4, hash EE9DF
+ buffers[277] = length 4, hash EE9DF
+ buffers[278] = length 4, hash EE9DF
+ buffers[279] = length 4, hash EE9DF
+ buffers[280] = length 4, hash EE9DF
+ buffers[281] = length 4, hash EE9DF
+ buffers[282] = length 4, hash EE9DF
+ buffers[283] = length 4, hash EE9DF
+ buffers[284] = length 4, hash EE9DF
+ buffers[285] = length 4, hash EE9DF
+ buffers[286] = length 4, hash EE9DF
+ buffers[287] = length 4, hash EE9DF
+ buffers[288] = length 4, hash EE9DF
+ buffers[289] = length 4, hash EE9DF
+ buffers[290] = length 4, hash EE9DF
+ buffers[291] = length 4, hash EE9DF
+ buffers[292] = length 4, hash EE9DF
+ buffers[293] = length 4, hash EE9DF
+ buffers[294] = length 4, hash EE9DF
+ buffers[295] = length 4, hash EE9DF
+ buffers[296] = length 4, hash EE9DF
+ buffers[297] = length 4, hash EE9DF
+ buffers[298] = length 4, hash EE9DF
+ buffers[299] = length 4, hash EE9DF
+ buffers[300] = length 4, hash EE9DF
+ buffers[301] = length 4, hash EE9DF
+ buffers[302] = length 4, hash EE9DF
+ buffers[303] = length 4, hash EE9DF
+ buffers[304] = length 4, hash EE9DF
+ buffers[305] = length 4, hash EE9DF
+ buffers[306] = length 4, hash EE9DF
+ buffers[307] = length 4, hash EE9DF
+ buffers[308] = length 4, hash EE9DF
+ buffers[309] = length 4, hash EE9DF
+ buffers[310] = length 4, hash EE9DF
+ buffers[311] = length 4, hash EE9DF
+ buffers[312] = length 4, hash EE9DF
+ buffers[313] = length 4, hash EE9DF
+ buffers[314] = length 4, hash EE9DF
+ buffers[315] = length 4, hash EE9DF
+ buffers[316] = length 4, hash EE9DF
+ buffers[317] = length 4, hash EE9DF
+ buffers[318] = length 4, hash EE9DF
+ buffers[319] = length 4, hash EE9DF
+ buffers[320] = length 4, hash EE9DF
+ buffers[321] = length 4, hash EE9DF
+ buffers[322] = length 4, hash EE9DF
+ buffers[323] = length 4, hash EE9DF
+ buffers[324] = length 4, hash EE9DF
+ buffers[325] = length 4, hash EE9DF
+ buffers[326] = length 4, hash EE9DF
+ buffers[327] = length 4, hash EE9DF
+ buffers[328] = length 4, hash EE9DF
+ buffers[329] = length 4, hash EE9DF
+ buffers[330] = length 4, hash EE9DF
+ buffers[331] = length 4, hash EE9DF
+ buffers[332] = length 4, hash EE9DF
+ buffers[333] = length 4, hash EE9DF
+ buffers[334] = length 4, hash EE9DF
+ buffers[335] = length 4, hash EE9DF
+ buffers[336] = length 4, hash EE9DF
+ buffers[337] = length 4, hash EE9DF
+ buffers[338] = length 4, hash EE9DF
+ buffers[339] = length 4, hash EE9DF
+ buffers[340] = length 4, hash EE9DF
+ buffers[341] = length 4, hash EE9DF
+ buffers[342] = length 4, hash EE9DF
+ buffers[343] = length 4, hash EE9DF
+ buffers[344] = length 4, hash EE9DF
+ buffers[345] = length 4, hash EE9DF
+ buffers[346] = length 4, hash EE9DF
+ buffers[347] = length 4, hash EE9DF
+ buffers[348] = length 4, hash EE9DF
+ buffers[349] = length 4, hash EE9DF
+ buffers[350] = length 4, hash EE9DF
+ buffers[351] = length 4, hash EE9DF
+ buffers[352] = length 4, hash EE9DF
+ buffers[353] = length 4, hash EE9DF
+ buffers[354] = length 4, hash EE9DF
+ buffers[355] = length 4, hash EE9DF
+ buffers[356] = length 4, hash EE9DF
+ buffers[357] = length 4, hash EE9DF
+ buffers[358] = length 4, hash EE9DF
+ buffers[359] = length 4, hash EE9DF
+ buffers[360] = length 4, hash EE9DF
+ buffers[361] = length 4, hash EE9DF
+ buffers[362] = length 4, hash EE9DF
+ buffers[363] = length 4, hash EE9DF
+ buffers[364] = length 4, hash EE9DF
+ buffers[365] = length 4, hash EE9DF
+ buffers[366] = length 4, hash EE9DF
+ buffers[367] = length 4, hash EE9DF
+ buffers[368] = length 4, hash EE9DF
+ buffers[369] = length 4, hash EE9DF
+ buffers[370] = length 4, hash EE9DF
+ buffers[371] = length 4, hash EE9DF
+ buffers[372] = length 4, hash EE9DF
+ buffers[373] = length 4, hash EE9DF
+ buffers[374] = length 4, hash EE9DF
+ buffers[375] = length 4, hash EE9DF
+ buffers[376] = length 4, hash EE9DF
+ buffers[377] = length 4, hash EE9DF
+ buffers[378] = length 4, hash EE9DF
+ buffers[379] = length 4, hash EE9DF
+ buffers[380] = length 4, hash EE9DF
+ buffers[381] = length 4, hash EE9DF
+ buffers[382] = length 4, hash EE9DF
+ buffers[383] = length 4, hash EE9DF
+ buffers[384] = length 4, hash EE9DF
+ buffers[385] = length 4, hash EE9DF
+ buffers[386] = length 4, hash EE9DF
+ buffers[387] = length 4, hash EE9DF
+ buffers[388] = length 4, hash EE9DF
+ buffers[389] = length 4, hash EE9DF
+ buffers[390] = length 4, hash EE9DF
+ buffers[391] = length 4, hash EE9DF
+ buffers[392] = length 4, hash EE9DF
+ buffers[393] = length 4, hash EE9DF
+ buffers[394] = length 4, hash EE9DF
+ buffers[395] = length 4, hash EE9DF
+ buffers[396] = length 4, hash EE9DF
+ buffers[397] = length 4, hash EE9DF
+ buffers[398] = length 4, hash EE9DF
+ buffers[399] = length 4, hash EE9DF
+ buffers[400] = length 4, hash EE9DF
+ buffers[401] = length 4, hash EE9DF
+ buffers[402] = length 4, hash EE9DF
+ buffers[403] = length 4, hash EE9DF
+ buffers[404] = length 4, hash EE9DF
+ buffers[405] = length 4, hash EE9DF
+ buffers[406] = length 4, hash EE9DF
+ buffers[407] = length 4, hash EE9DF
+ buffers[408] = length 4, hash EE9DF
+ buffers[409] = length 4, hash EE9DF
+ buffers[410] = length 4, hash EE9DF
+ buffers[411] = length 4, hash EE9DF
+ buffers[412] = length 4, hash EE9DF
+ buffers[413] = length 4, hash EE9DF
+ buffers[414] = length 4, hash EE9DF
+ buffers[415] = length 4, hash EE9DF
+ buffers[416] = length 4, hash EE9DF
+ buffers[417] = length 4, hash EE9DF
+ buffers[418] = length 4, hash EE9DF
+ buffers[419] = length 4, hash EE9DF
+ buffers[420] = length 4, hash EE9DF
+ buffers[421] = length 4, hash EE9DF
+ buffers[422] = length 4, hash EE9DF
+ buffers[423] = length 4, hash EE9DF
+ buffers[424] = length 4, hash EE9DF
+ buffers[425] = length 4, hash EE9DF
+ buffers[426] = length 4, hash EE9DF
+ buffers[427] = length 4, hash EE9DF
+ buffers[428] = length 4, hash EE9DF
+ buffers[429] = length 4, hash EE9DF
+ buffers[430] = length 4, hash EE9DF
+ buffers[431] = length 4, hash EE9DF
+ buffers[432] = length 4, hash EE9DF
+ buffers[433] = length 4, hash EE9DF
+ buffers[434] = length 4, hash EE9DF
+ buffers[435] = length 0, hash 1
+MediaCodecAdapter (exotest.video.avc):
+ buffers.length = 251
+ buffers[0] = length 5245, hash C090A41E
+ buffers[1] = length 63, hash 5141C80D
+ buffers[2] = length 22, hash A32E59A1
+ buffers[3] = length 20, hash A09DEAB8
+ buffers[4] = length 18, hash B64DA059
+ buffers[5] = length 28, hash FC8EF2BB
+ buffers[6] = length 22, hash BF8A4A9F
+ buffers[7] = length 18, hash D163DF61
+ buffers[8] = length 18, hash FD82E95
+ buffers[9] = length 28, hash 44A16E72
+ buffers[10] = length 22, hash 31C06057
+ buffers[11] = length 18, hash DC93CC9D
+ buffers[12] = length 18, hash 1B081BD1
+ buffers[13] = length 28, hash 2700AF
+ buffers[14] = length 22, hash 6D292D94
+ buffers[15] = length 18, hash D646C05A
+ buffers[16] = length 18, hash 14BB0F8E
+ buffers[17] = length 28, hash 5DE2C2B
+ buffers[18] = length 22, hash 57E81CD0
+ buffers[19] = length 18, hash E176AD96
+ buffers[20] = length 18, hash 1FEAFCCA
+ buffers[21] = length 28, hash C163BE68
+ buffers[22] = length 22, hash B0C92D0B
+ buffers[23] = length 18, hash 3B013BD2
+ buffers[24] = length 18, hash 79758B06
+ buffers[25] = length 28, hash F72EB1A3
+ buffers[26] = length 22, hash 9B881C48
+ buffers[27] = length 18, hash 4631290E
+ buffers[28] = length 18, hash 84A57842
+ buffers[29] = length 28, hash E1FCF000
+ buffers[30] = length 22, hash 359D2D82
+ buffers[31] = length 18, hash 62DE0FC9
+ buffers[32] = length 18, hash A1525EFD
+ buffers[33] = length 28, hash 5350E8FA
+ buffers[34] = length 22, hash EE2060DF
+ buffers[35] = length 18, hash 77D95125
+ buffers[36] = length 18, hash B64DA059
+ buffers[37] = length 28, hash ED67B37
+ buffers[38] = length 22, hash 4701711B
+ buffers[39] = length 18, hash D163DF61
+ buffers[40] = length 18, hash FD82E95
+ buffers[41] = length 28, hash 44A16E72
+ buffers[42] = length 22, hash 31C06057
+ buffers[43] = length 18, hash DC93CC9D
+ buffers[44] = length 18, hash 1B081BD1
+ buffers[45] = length 28, hash 2700AF
+ buffers[46] = length 22, hash 6D292D94
+ buffers[47] = length 18, hash D646C05A
+ buffers[48] = length 18, hash 14BB0F8E
+ buffers[49] = length 28, hash 5DE2C2B
+ buffers[50] = length 22, hash 57E81CD0
+ buffers[51] = length 18, hash E176AD96
+ buffers[52] = length 18, hash 1FEAFCCA
+ buffers[53] = length 28, hash C163BE68
+ buffers[54] = length 22, hash B0C92D0B
+ buffers[55] = length 18, hash 3B013BD2
+ buffers[56] = length 18, hash 79758B06
+ buffers[57] = length 28, hash F72EB1A3
+ buffers[58] = length 22, hash 9B881C48
+ buffers[59] = length 18, hash 4631290E
+ buffers[60] = length 18, hash 84A57842
+ buffers[61] = length 28, hash E1FCF000
+ buffers[62] = length 22, hash 359D2D82
+ buffers[63] = length 18, hash 62DE0FC9
+ buffers[64] = length 18, hash A1525EFD
+ buffers[65] = length 28, hash 5350E8FA
+ buffers[66] = length 22, hash EE2060DF
+ buffers[67] = length 18, hash 77D95125
+ buffers[68] = length 18, hash B64DA059
+ buffers[69] = length 28, hash ED67B37
+ buffers[70] = length 22, hash 4701711B
+ buffers[71] = length 18, hash D163DF61
+ buffers[72] = length 18, hash FD82E95
+ buffers[73] = length 28, hash 44A16E72
+ buffers[74] = length 22, hash 31C06057
+ buffers[75] = length 18, hash DC93CC9D
+ buffers[76] = length 18, hash 1B081BD1
+ buffers[77] = length 28, hash 2700AF
+ buffers[78] = length 22, hash 6D292D94
+ buffers[79] = length 18, hash D646C05A
+ buffers[80] = length 18, hash 14BB0F8E
+ buffers[81] = length 28, hash 5DE2C2B
+ buffers[82] = length 22, hash 57E81CD0
+ buffers[83] = length 18, hash E176AD96
+ buffers[84] = length 18, hash 1FEAFCCA
+ buffers[85] = length 28, hash C163BE68
+ buffers[86] = length 22, hash B0C92D0B
+ buffers[87] = length 18, hash 3B013BD2
+ buffers[88] = length 18, hash 79758B06
+ buffers[89] = length 28, hash F72EB1A3
+ buffers[90] = length 22, hash 9B881C48
+ buffers[91] = length 18, hash 4631290E
+ buffers[92] = length 18, hash 84A57842
+ buffers[93] = length 33, hash AF5CF49E
+ buffers[94] = length 22, hash 359D2D82
+ buffers[95] = length 18, hash 62DE0FC9
+ buffers[96] = length 18, hash A1525EFD
+ buffers[97] = length 33, hash F4C6DE46
+ buffers[98] = length 22, hash EE2060DF
+ buffers[99] = length 18, hash 77D95125
+ buffers[100] = length 18, hash B64DA059
+ buffers[101] = length 28, hash ED67B37
+ buffers[102] = length 22, hash 4701711B
+ buffers[103] = length 18, hash D163DF61
+ buffers[104] = length 18, hash FD82E95
+ buffers[105] = length 28, hash 44A16E72
+ buffers[106] = length 22, hash 31C06057
+ buffers[107] = length 18, hash DC93CC9D
+ buffers[108] = length 18, hash 1B081BD1
+ buffers[109] = length 28, hash 2700AF
+ buffers[110] = length 22, hash 6D292D94
+ buffers[111] = length 18, hash D646C05A
+ buffers[112] = length 18, hash 14BB0F8E
+ buffers[113] = length 27, hash 5292D9E
+ buffers[114] = length 22, hash 57E81CD0
+ buffers[115] = length 18, hash E176AD96
+ buffers[116] = length 18, hash 1FEAFCCA
+ buffers[117] = length 26, hash B0CAA4C9
+ buffers[118] = length 22, hash B0C92D0B
+ buffers[119] = length 18, hash 3B013BD2
+ buffers[120] = length 18, hash 79758B06
+ buffers[121] = length 26, hash C63A1445
+ buffers[122] = length 22, hash 9B881C48
+ buffers[123] = length 18, hash 4631290E
+ buffers[124] = length 18, hash 84A57842
+ buffers[125] = length 5245, hash C090A41E
+ buffers[126] = length 63, hash 5141C80D
+ buffers[127] = length 22, hash A32E59A1
+ buffers[128] = length 20, hash A09DEAB8
+ buffers[129] = length 18, hash B64DA059
+ buffers[130] = length 28, hash FC8EF2BB
+ buffers[131] = length 22, hash BF8A4A9F
+ buffers[132] = length 18, hash D163DF61
+ buffers[133] = length 18, hash FD82E95
+ buffers[134] = length 28, hash 44A16E72
+ buffers[135] = length 22, hash 31C06057
+ buffers[136] = length 18, hash DC93CC9D
+ buffers[137] = length 18, hash 1B081BD1
+ buffers[138] = length 28, hash 2700AF
+ buffers[139] = length 22, hash 6D292D94
+ buffers[140] = length 18, hash D646C05A
+ buffers[141] = length 18, hash 14BB0F8E
+ buffers[142] = length 28, hash 5DE2C2B
+ buffers[143] = length 22, hash 57E81CD0
+ buffers[144] = length 18, hash E176AD96
+ buffers[145] = length 18, hash 1FEAFCCA
+ buffers[146] = length 28, hash C163BE68
+ buffers[147] = length 22, hash B0C92D0B
+ buffers[148] = length 18, hash 3B013BD2
+ buffers[149] = length 18, hash 79758B06
+ buffers[150] = length 28, hash F72EB1A3
+ buffers[151] = length 22, hash 9B881C48
+ buffers[152] = length 18, hash 4631290E
+ buffers[153] = length 18, hash 84A57842
+ buffers[154] = length 28, hash E1FCF000
+ buffers[155] = length 22, hash 359D2D82
+ buffers[156] = length 18, hash 62DE0FC9
+ buffers[157] = length 18, hash A1525EFD
+ buffers[158] = length 28, hash 5350E8FA
+ buffers[159] = length 22, hash EE2060DF
+ buffers[160] = length 18, hash 77D95125
+ buffers[161] = length 18, hash B64DA059
+ buffers[162] = length 28, hash ED67B37
+ buffers[163] = length 22, hash 4701711B
+ buffers[164] = length 18, hash D163DF61
+ buffers[165] = length 18, hash FD82E95
+ buffers[166] = length 28, hash 44A16E72
+ buffers[167] = length 22, hash 31C06057
+ buffers[168] = length 18, hash DC93CC9D
+ buffers[169] = length 18, hash 1B081BD1
+ buffers[170] = length 28, hash 2700AF
+ buffers[171] = length 22, hash 6D292D94
+ buffers[172] = length 18, hash D646C05A
+ buffers[173] = length 18, hash 14BB0F8E
+ buffers[174] = length 28, hash 5DE2C2B
+ buffers[175] = length 22, hash 57E81CD0
+ buffers[176] = length 18, hash E176AD96
+ buffers[177] = length 18, hash 1FEAFCCA
+ buffers[178] = length 28, hash C163BE68
+ buffers[179] = length 22, hash B0C92D0B
+ buffers[180] = length 18, hash 3B013BD2
+ buffers[181] = length 18, hash 79758B06
+ buffers[182] = length 28, hash F72EB1A3
+ buffers[183] = length 22, hash 9B881C48
+ buffers[184] = length 18, hash 4631290E
+ buffers[185] = length 18, hash 84A57842
+ buffers[186] = length 28, hash E1FCF000
+ buffers[187] = length 22, hash 359D2D82
+ buffers[188] = length 18, hash 62DE0FC9
+ buffers[189] = length 18, hash A1525EFD
+ buffers[190] = length 28, hash 5350E8FA
+ buffers[191] = length 22, hash EE2060DF
+ buffers[192] = length 18, hash 77D95125
+ buffers[193] = length 18, hash B64DA059
+ buffers[194] = length 28, hash ED67B37
+ buffers[195] = length 22, hash 4701711B
+ buffers[196] = length 18, hash D163DF61
+ buffers[197] = length 18, hash FD82E95
+ buffers[198] = length 28, hash 44A16E72
+ buffers[199] = length 22, hash 31C06057
+ buffers[200] = length 18, hash DC93CC9D
+ buffers[201] = length 18, hash 1B081BD1
+ buffers[202] = length 28, hash 2700AF
+ buffers[203] = length 22, hash 6D292D94
+ buffers[204] = length 18, hash D646C05A
+ buffers[205] = length 18, hash 14BB0F8E
+ buffers[206] = length 28, hash 5DE2C2B
+ buffers[207] = length 22, hash 57E81CD0
+ buffers[208] = length 18, hash E176AD96
+ buffers[209] = length 18, hash 1FEAFCCA
+ buffers[210] = length 28, hash C163BE68
+ buffers[211] = length 22, hash B0C92D0B
+ buffers[212] = length 18, hash 3B013BD2
+ buffers[213] = length 18, hash 79758B06
+ buffers[214] = length 28, hash F72EB1A3
+ buffers[215] = length 22, hash 9B881C48
+ buffers[216] = length 18, hash 4631290E
+ buffers[217] = length 18, hash 84A57842
+ buffers[218] = length 33, hash AF5CF49E
+ buffers[219] = length 22, hash 359D2D82
+ buffers[220] = length 18, hash 62DE0FC9
+ buffers[221] = length 18, hash A1525EFD
+ buffers[222] = length 33, hash F4C6DE46
+ buffers[223] = length 22, hash EE2060DF
+ buffers[224] = length 18, hash 77D95125
+ buffers[225] = length 18, hash B64DA059
+ buffers[226] = length 28, hash ED67B37
+ buffers[227] = length 22, hash 4701711B
+ buffers[228] = length 18, hash D163DF61
+ buffers[229] = length 18, hash FD82E95
+ buffers[230] = length 28, hash 44A16E72
+ buffers[231] = length 22, hash 31C06057
+ buffers[232] = length 18, hash DC93CC9D
+ buffers[233] = length 18, hash 1B081BD1
+ buffers[234] = length 28, hash 2700AF
+ buffers[235] = length 22, hash 6D292D94
+ buffers[236] = length 18, hash D646C05A
+ buffers[237] = length 18, hash 14BB0F8E
+ buffers[238] = length 27, hash 5292D9E
+ buffers[239] = length 22, hash 57E81CD0
+ buffers[240] = length 18, hash E176AD96
+ buffers[241] = length 18, hash 1FEAFCCA
+ buffers[242] = length 26, hash B0CAA4C9
+ buffers[243] = length 22, hash B0C92D0B
+ buffers[244] = length 18, hash 3B013BD2
+ buffers[245] = length 18, hash 79758B06
+ buffers[246] = length 26, hash C63A1445
+ buffers[247] = length 22, hash 9B881C48
+ buffers[248] = length 18, hash 4631290E
+ buffers[249] = length 18, hash 84A57842
+ buffers[250] = length 0, hash 1
+TextOutput:
+ Subtitle[0]:
+ presentationTimeUs = 0
+ Cues = []
+ Subtitle[1]:
+ presentationTimeUs = 0
+ Cue[0]:
+ text = This is the first subtitle.
+ textAlignment = ALIGN_CENTER
+ line = -1.0
+ lineType = 1
+ lineAnchor = 0
+ position = 0.5
+ positionAnchor = 1
+ size = 1.0
+ Subtitle[2]:
+ presentationTimeUs = 1234000
+ Cues = []
+ Subtitle[3]:
+ presentationTimeUs = 2345000
+ Cue[0]:
+ text = This is the second subtitle.
+ textAlignment = ALIGN_CENTER
+ line = -1.0
+ lineType = 1
+ lineAnchor = 0
+ position = 0.5
+ positionAnchor = 1
+ size = 1.0
+ Subtitle[4]:
+ presentationTimeUs = 3456000
+ Cues = []
diff --git a/libraries/test_data/src/test/assets/playbackdumps/webvtt/typical.dump b/libraries/test_data/src/test/assets/playbackdumps/webvtt/typical.dump
index db4b4047c5..bc1e7b954a 100644
--- a/libraries/test_data/src/test/assets/playbackdumps/webvtt/typical.dump
+++ b/libraries/test_data/src/test/assets/playbackdumps/webvtt/typical.dump
@@ -348,8 +348,10 @@ MediaCodecAdapter (exotest.video.avc):
buffers[125] = length 0, hash 1
TextOutput:
Subtitle[0]:
+ presentationTimeUs = 0
Cues = []
Subtitle[1]:
+ presentationTimeUs = 0
Cue[0]:
text = This is the first subtitle.
textAlignment = ALIGN_CENTER
@@ -360,8 +362,10 @@ TextOutput:
positionAnchor = 1
size = 1.0
Subtitle[2]:
+ presentationTimeUs = 1234000
Cues = []
Subtitle[3]:
+ presentationTimeUs = 2345000
Cue[0]:
text = This is the second subtitle.
textAlignment = ALIGN_CENTER
@@ -372,4 +376,5 @@ TextOutput:
positionAnchor = 1
size = 1.0
Subtitle[4]:
+ presentationTimeUs = 3456000
Cues = []
diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java
index 2cfaf781b6..b84b90a9a6 100644
--- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java
+++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java
@@ -1920,7 +1920,7 @@ public class MediaControllerListenerTest {
Bundle playerConfig =
new RemoteMediaSession.MockPlayerConfigBuilder()
- .setCurrentCues(new CueGroup(testCues))
+ .setCurrentCues(new CueGroup(testCues, /* presentationTimeUs= */ 1_230_000))
.build();
remoteSession.setPlayer(playerConfig);
@@ -1938,7 +1938,7 @@ public class MediaControllerListenerTest {
Bundle playerConfig =
new RemoteMediaSession.MockPlayerConfigBuilder()
- .setCurrentCues(new CueGroup(testCues))
+ .setCurrentCues(new CueGroup(testCues, /* presentationTimeUs= */ 1_230_000))
.build();
remoteSession.setPlayer(playerConfig);
@@ -1991,7 +1991,7 @@ public class MediaControllerListenerTest {
Bundle playerConfig =
new RemoteMediaSession.MockPlayerConfigBuilder()
- .setCurrentCues(new CueGroup(testCues))
+ .setCurrentCues(new CueGroup(testCues, /* presentationTimeUs= */ 1_230_000))
.build();
remoteSession.setPlayer(playerConfig);
@@ -2021,7 +2021,9 @@ public class MediaControllerListenerTest {
};
threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener));
- remoteSession.getMockPlayer().notifyCuesChanged(new CueGroup(testCues));
+ remoteSession
+ .getMockPlayer()
+ .notifyCuesChanged(new CueGroup(testCues, /* presentationTimeUs= */ 1_230_000));
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(cuesFromParam).isEqualTo(testCues);
diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java
index 6bf44ca49f..fa4605cfaa 100644
--- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java
+++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java
@@ -105,6 +105,8 @@ import java.util.concurrent.Callable;
public class MediaSessionProviderService extends Service {
private static final String TAG = "MSProviderService";
+ private static final CueGroup EMPTY_CUE_GROUP =
+ new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
private Map sessionMap = new HashMap<>();
private RemoteMediaSessionStub sessionBinder;
@@ -327,7 +329,7 @@ public class MediaSessionProviderService extends Service {
}
Bundle cueGroupBundle = config.getBundle(KEY_CURRENT_CUE_GROUP);
player.cueGroup =
- cueGroupBundle == null ? CueGroup.EMPTY : CueGroup.CREATOR.fromBundle(cueGroupBundle);
+ cueGroupBundle == null ? EMPTY_CUE_GROUP : CueGroup.CREATOR.fromBundle(cueGroupBundle);
@Nullable Bundle deviceInfoBundle = config.getBundle(KEY_DEVICE_INFO);
if (deviceInfoBundle != null) {
player.deviceInfo = DeviceInfo.CREATOR.fromBundle(deviceInfoBundle);
diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java
index d9a9258b65..ee26b18e33 100644
--- a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java
+++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java
@@ -196,6 +196,8 @@ public class MockPlayer implements Player {
private final ArraySet listeners = new ArraySet<>();
private final ImmutableMap<@Method Integer, ConditionVariable> conditionVariables =
createMethodConditionVariables();
+ private static final CueGroup EMPTY_CUE_GROUP =
+ new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
@Nullable PlaybackException playerError;
public AudioAttributes audioAttributes;
@@ -276,7 +278,7 @@ public class MockPlayer implements Player {
repeatMode = Player.REPEAT_MODE_OFF;
videoSize = VideoSize.UNKNOWN;
volume = 1.0f;
- cueGroup = CueGroup.EMPTY;
+ cueGroup = EMPTY_CUE_GROUP;
deviceInfo = DeviceInfo.UNKNOWN;
seekPositionMs = C.TIME_UNSET;
seekMediaItemIndex = C.INDEX_UNSET;
diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java
index 947ab7d82c..8fc015fc9c 100644
--- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java
+++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java
@@ -15,6 +15,8 @@
*/
package androidx.media3.test.utils.robolectric;
+import static java.lang.Math.max;
+
import android.graphics.Bitmap;
import androidx.annotation.Nullable;
import androidx.media3.common.Metadata;
@@ -56,7 +58,7 @@ public final class PlaybackOutput implements Dumper.Dumpable {
private final CapturingRenderersFactory capturingRenderersFactory;
private final List metadatas;
- private final List> subtitles;
+ private final List subtitles;
private final List> subtitlesFromDeprecatedTextOutput;
private PlaybackOutput(ExoPlayer player, CapturingRenderersFactory capturingRenderersFactory) {
@@ -65,8 +67,8 @@ public final class PlaybackOutput implements Dumper.Dumpable {
metadatas = Collections.synchronizedList(new ArrayList<>());
subtitles = Collections.synchronizedList(new ArrayList<>());
subtitlesFromDeprecatedTextOutput = Collections.synchronizedList(new ArrayList<>());
- // TODO: Consider passing playback position into MetadataOutput and TextOutput. Calling
- // player.getCurrentPosition() inside onMetadata/Cues will likely be non-deterministic
+ // TODO: Consider passing playback position into MetadataOutput. Calling
+ // player.getCurrentPosition() inside onMetadata will likely be non-deterministic
// because renderer-thread != playback-thread.
player.addListener(
new Player.Listener() {
@@ -82,7 +84,7 @@ public final class PlaybackOutput implements Dumper.Dumpable {
@Override
public void onCues(CueGroup cueGroup) {
- subtitles.add(cueGroup.cues);
+ subtitles.add(cueGroup);
}
});
}
@@ -154,9 +156,9 @@ public final class PlaybackOutput implements Dumper.Dumpable {
}
private void dumpSubtitles(Dumper dumper) {
- if (!subtitles.equals(subtitlesFromDeprecatedTextOutput)) {
+ if (subtitles.size() != subtitlesFromDeprecatedTextOutput.size()) {
throw new IllegalStateException(
- "Expected subtitles to be equal from both implementations of onCues method.");
+ "Expected subtitles to be of equal length from both implementations of onCues method.");
}
if (subtitles.isEmpty()) {
@@ -165,7 +167,15 @@ public final class PlaybackOutput implements Dumper.Dumpable {
dumper.startBlock("TextOutput");
for (int i = 0; i < subtitles.size(); i++) {
dumper.startBlock("Subtitle[" + i + "]");
- List subtitle = subtitles.get(i);
+ // TODO: Solving https://github.com/google/ExoPlayer/issues/9672 will allow us to remove this
+ // hack of forcing presentationTimeUs to be >= 0.
+ dumper.add("presentationTimeUs", max(0, subtitles.get(i).presentationTimeUs));
+ ImmutableList subtitle = subtitles.get(i).cues;
+ if (!subtitle.equals(subtitlesFromDeprecatedTextOutput.get(i))) {
+ throw new IllegalStateException(
+ "Expected subtitle to be equal from both implementations of onCues method for index "
+ + i);
+ }
if (subtitle.isEmpty()) {
dumper.add("Cues", ImmutableList.of());
}