Add timestamp to CueGroup

`TextRenderer` is updated to output `CueGroup`, which contains the presentation time of the cues, in microseconds.

PiperOrigin-RevId: 456531399
This commit is contained in:
rohks 2022-06-22 17:19:55 +01:00 committed by Ian Baker
parent 69ab79418e
commit 74d61bbffb
15 changed files with 885 additions and 42 deletions

View File

@ -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}. */

View File

@ -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 {
* <p>This list may be empty if the group represents a state with no cues.
*/
public final ImmutableList<Cue> cues;
/**
* The presentation time of the {@link #cues}, in microseconds.
*
* <p>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<Cue> cues) {
public CueGroup(List<Cue> 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) {

View File

@ -37,7 +37,7 @@ public class CueGroupTest {
Cue bitmapCue =
new Cue.Builder().setBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)).build();
ImmutableList<Cue> cues = ImmutableList.of(textCue, bitmapCue);
CueGroup cueGroup = new CueGroup(cues);
CueGroup cueGroup = new CueGroup(cues, /* presentationTimeUs= */ 1_230_000);
Parcel parcel = Parcel.obtain();
try {

View File

@ -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;
}

View File

@ -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<Cue> 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<Cue>) msg.obj);
invokeUpdateOutputInternal((CueGroup) msg.obj);
return true;
default:
throw new IllegalStateException();
}
}
private void invokeUpdateOutputInternal(List<Cue> 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;
}
}

View File

@ -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");
}
}

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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 = []

View File

@ -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 = []

View File

@ -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);

View File

@ -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<String, MediaSession> 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);

View File

@ -196,6 +196,8 @@ public class MockPlayer implements Player {
private final ArraySet<Listener> 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;

View File

@ -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<Metadata> metadatas;
private final List<List<Cue>> subtitles;
private final List<CueGroup> subtitles;
private final List<List<Cue>> 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<Cue> 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<Cue> 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());
}