Commit 1bc316de by rohks Committed by microkatz

Add timestamp to `CueGroup`

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

PiperOrigin-RevId: 456531399
(cherry picked from commit bf11a8a8)
parent 6edd1d2a
...@@ -111,6 +111,8 @@ public final class CastPlayer extends BasePlayer { ...@@ -111,6 +111,8 @@ public final class CastPlayer extends BasePlayer {
private static final long PROGRESS_REPORT_PERIOD_MS = 1000; private static final long PROGRESS_REPORT_PERIOD_MS = 1000;
private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0]; 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 CastContext castContext;
private final MediaItemConverter mediaItemConverter; private final MediaItemConverter mediaItemConverter;
...@@ -724,7 +726,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -724,7 +726,7 @@ public final class CastPlayer extends BasePlayer {
/** This method is not supported and returns an empty {@link CueGroup}. */ /** This method is not supported and returns an empty {@link CueGroup}. */
@Override @Override
public CueGroup getCurrentCues() { public CueGroup getCurrentCues() {
return CueGroup.EMPTY; return EMPTY_CUE_GROUP;
} }
/** This method is not supported and always returns {@link DeviceInfo#UNKNOWN}. */ /** This method is not supported and always returns {@link DeviceInfo#UNKNOWN}. */
......
...@@ -22,6 +22,7 @@ import android.os.Bundle; ...@@ -22,6 +22,7 @@ import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable; import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.BundleableUtil; import com.google.android.exoplayer2.util.BundleableUtil;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
...@@ -33,10 +34,6 @@ import java.util.List; ...@@ -33,10 +34,6 @@ import java.util.List;
/** Class to represent the state of active {@link Cue Cues} at a particular time. */ /** Class to represent the state of active {@link Cue Cues} at a particular time. */
public final class CueGroup implements Bundleable { public final class CueGroup implements Bundleable {
/** Empty {@link CueGroup}. */
public static final CueGroup EMPTY = new CueGroup(ImmutableList.of());
/** /**
* The cues in this group. * The cues in this group.
* *
...@@ -46,10 +43,17 @@ public final class CueGroup implements Bundleable { ...@@ -46,10 +43,17 @@ public final class CueGroup implements Bundleable {
* <p>This list may be empty if the group represents a state with no cues. * <p>This list may be empty if the group represents a state with no cues.
*/ */
public final ImmutableList<Cue> 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}
*/
public final long presentationTimeUs;
/** Creates a CueGroup. */ /** Creates a CueGroup. */
public CueGroup(List<Cue> cues) { public CueGroup(List<Cue> cues, long presentationTimeUs) {
this.cues = ImmutableList.copyOf(cues); this.cues = ImmutableList.copyOf(cues);
this.presentationTimeUs = presentationTimeUs;
} }
// Bundleable implementation. // Bundleable implementation.
...@@ -57,16 +61,18 @@ public final class CueGroup implements Bundleable { ...@@ -57,16 +61,18 @@ public final class CueGroup implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE) @Target(TYPE_USE)
@IntDef({FIELD_CUES}) @IntDef({FIELD_CUES, FIELD_PRESENTATION_TIME_US})
private @interface FieldNumber {} private @interface FieldNumber {}
private static final int FIELD_CUES = 0; private static final int FIELD_CUES = 0;
private static final int FIELD_PRESENTATION_TIME_US = 1;
@Override @Override
public Bundle toBundle() { public Bundle toBundle() {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putParcelableArrayList( bundle.putParcelableArrayList(
keyForField(FIELD_CUES), BundleableUtil.toBundleArrayList(filterOutBitmapCues(cues))); keyForField(FIELD_CUES), BundleableUtil.toBundleArrayList(filterOutBitmapCues(cues)));
bundle.putLong(keyForField(FIELD_PRESENTATION_TIME_US), presentationTimeUs);
return bundle; return bundle;
} }
...@@ -78,7 +84,8 @@ public final class CueGroup implements Bundleable { ...@@ -78,7 +84,8 @@ public final class CueGroup implements Bundleable {
cueBundles == null cueBundles == null
? ImmutableList.of() ? ImmutableList.of()
: BundleableUtil.fromBundleList(Cue.CREATOR, cueBundles); : 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) { private static String keyForField(@FieldNumber int field) {
......
...@@ -37,7 +37,7 @@ public class CueGroupTest { ...@@ -37,7 +37,7 @@ public class CueGroupTest {
Cue bitmapCue = Cue bitmapCue =
new Cue.Builder().setBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)).build(); new Cue.Builder().setBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)).build();
ImmutableList<Cue> cues = ImmutableList.of(textCue, bitmapCue); 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(); Parcel parcel = Parcel.obtain();
try { try {
......
...@@ -344,7 +344,7 @@ import java.util.concurrent.TimeoutException; ...@@ -344,7 +344,7 @@ import java.util.concurrent.TimeoutException;
} else { } else {
audioSessionId = Util.generateAudioSessionIdV21(applicationContext); audioSessionId = Util.generateAudioSessionIdV21(applicationContext);
} }
currentCueGroup = CueGroup.EMPTY; currentCueGroup = new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
throwsWhenUsingWrongThread = true; throwsWhenUsingWrongThread = true;
addListener(analyticsCollector); addListener(analyticsCollector);
...@@ -931,7 +931,7 @@ import java.util.concurrent.TimeoutException; ...@@ -931,7 +931,7 @@ import java.util.concurrent.TimeoutException;
verifyApplicationThread(); verifyApplicationThread();
audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE); audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE);
stopInternal(reset, /* error= */ null); stopInternal(reset, /* error= */ null);
currentCueGroup = CueGroup.EMPTY; currentCueGroup = new CueGroup(ImmutableList.of(), playbackInfo.positionUs);
} }
@Override @Override
...@@ -985,7 +985,7 @@ import java.util.concurrent.TimeoutException; ...@@ -985,7 +985,7 @@ import java.util.concurrent.TimeoutException;
checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK);
isPriorityTaskManagerRegistered = false; isPriorityTaskManagerRegistered = false;
} }
currentCueGroup = CueGroup.EMPTY; currentCueGroup = new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
playerReleased = true; playerReleased = true;
} }
......
...@@ -34,12 +34,13 @@ import com.google.android.exoplayer2.source.SampleStream.ReadDataResult; ...@@ -34,12 +34,13 @@ import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.Collections; import org.checkerframework.checker.nullness.qual.RequiresNonNull;
import java.util.List; import org.checkerframework.dataflow.qual.SideEffectFree;
/** /**
* A renderer for text. * A renderer for text.
...@@ -94,6 +95,8 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -94,6 +95,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Nullable private SubtitleOutputBuffer nextSubtitle; @Nullable private SubtitleOutputBuffer nextSubtitle;
private int nextSubtitleEventIndex; private int nextSubtitleEventIndex;
private long finalStreamEndPositionUs; private long finalStreamEndPositionUs;
private long outputStreamOffsetUs;
private long lastRendererPositionUs;
/** /**
* @param output The output. * @param output The output.
...@@ -125,6 +128,8 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -125,6 +128,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
this.decoderFactory = decoderFactory; this.decoderFactory = decoderFactory;
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
finalStreamEndPositionUs = C.TIME_UNSET; finalStreamEndPositionUs = C.TIME_UNSET;
outputStreamOffsetUs = C.TIME_UNSET;
lastRendererPositionUs = C.TIME_UNSET;
} }
@Override @Override
...@@ -161,6 +166,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -161,6 +166,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override @Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) { protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
outputStreamOffsetUs = offsetUs;
streamFormat = formats[0]; streamFormat = formats[0];
if (decoder != null) { if (decoder != null) {
decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM; decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM;
...@@ -171,6 +177,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -171,6 +177,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override @Override
protected void onPositionReset(long positionUs, boolean joining) { protected void onPositionReset(long positionUs, boolean joining) {
lastRendererPositionUs = positionUs;
clearOutput(); clearOutput();
inputStreamEnded = false; inputStreamEnded = false;
outputStreamEnded = false; outputStreamEnded = false;
...@@ -185,6 +192,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -185,6 +192,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override @Override
public void render(long positionUs, long elapsedRealtimeUs) { public void render(long positionUs, long elapsedRealtimeUs) {
lastRendererPositionUs = positionUs;
if (isCurrentStreamFinal() if (isCurrentStreamFinal()
&& finalStreamEndPositionUs != C.TIME_UNSET && finalStreamEndPositionUs != C.TIME_UNSET
&& positionUs >= finalStreamEndPositionUs) { && positionUs >= finalStreamEndPositionUs) {
...@@ -248,7 +256,9 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -248,7 +256,9 @@ public final class TextRenderer extends BaseRenderer implements Callback {
// If textRendererNeedsUpdate then subtitle must be non-null. // If textRendererNeedsUpdate then subtitle must be non-null.
checkNotNull(subtitle); checkNotNull(subtitle);
// textRendererNeedsUpdate is set and we're playing. Update the renderer. // 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) { if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) {
...@@ -306,6 +316,8 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -306,6 +316,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
streamFormat = null; streamFormat = null;
finalStreamEndPositionUs = C.TIME_UNSET; finalStreamEndPositionUs = C.TIME_UNSET;
clearOutput(); clearOutput();
outputStreamOffsetUs = C.TIME_UNSET;
lastRendererPositionUs = C.TIME_UNSET;
releaseDecoder(); releaseDecoder();
} }
...@@ -361,33 +373,33 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -361,33 +373,33 @@ public final class TextRenderer extends BaseRenderer implements Callback {
: subtitle.getEventTime(nextSubtitleEventIndex); : subtitle.getEventTime(nextSubtitleEventIndex);
} }
private void updateOutput(List<Cue> cues) { private void updateOutput(CueGroup cueGroup) {
if (outputHandler != null) { if (outputHandler != null) {
outputHandler.obtainMessage(MSG_UPDATE_OUTPUT, cues).sendToTarget(); outputHandler.obtainMessage(MSG_UPDATE_OUTPUT, cueGroup).sendToTarget();
} else { } else {
invokeUpdateOutputInternal(cues); invokeUpdateOutputInternal(cueGroup);
} }
} }
private void clearOutput() { private void clearOutput() {
updateOutput(Collections.emptyList()); updateOutput(new CueGroup(ImmutableList.of(), getPresentationTimeUs(lastRendererPositionUs)));
} }
@SuppressWarnings("unchecked")
@Override @Override
public boolean handleMessage(Message msg) { public boolean handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
case MSG_UPDATE_OUTPUT: case MSG_UPDATE_OUTPUT:
invokeUpdateOutputInternal((List<Cue>) msg.obj); invokeUpdateOutputInternal((CueGroup) msg.obj);
return true; return true;
default: default:
throw new IllegalStateException(); throw new IllegalStateException();
} }
} }
private void invokeUpdateOutputInternal(List<Cue> cues) { @SuppressWarnings("deprecation") // We need to call both onCues method for backward compatibility.
output.onCues(cues); private void invokeUpdateOutputInternal(CueGroup cueGroup) {
output.onCues(new CueGroup(cues)); output.onCues(cueGroup.cues);
output.onCues(cueGroup);
} }
/** /**
...@@ -401,4 +413,25 @@ public final class TextRenderer extends BaseRenderer implements Callback { ...@@ -401,4 +413,25 @@ public final class TextRenderer extends BaseRenderer implements Callback {
clearOutput(); clearOutput();
replaceDecoder(); 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;
}
} }
...@@ -16,17 +16,25 @@ ...@@ -16,17 +16,25 @@
package com.google.android.exoplayer2.e2etest; package com.google.android.exoplayer2.e2etest;
import android.content.Context; import android.content.Context;
import android.graphics.SurfaceTexture;
import android.net.Uri;
import android.view.Surface;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.robolectric.PlaybackOutput; import com.google.android.exoplayer2.robolectric.PlaybackOutput;
import com.google.android.exoplayer2.robolectric.ShadowMediaCodecConfig; import com.google.android.exoplayer2.robolectric.ShadowMediaCodecConfig;
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper; import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.testutil.CapturingRenderersFactory; import com.google.android.exoplayer2.testutil.CapturingRenderersFactory;
import com.google.android.exoplayer2.testutil.DumpFileAsserts; import com.google.android.exoplayer2.testutil.DumpFileAsserts;
import com.google.android.exoplayer2.testutil.FakeClock; import com.google.android.exoplayer2.testutil.FakeClock;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -82,4 +90,43 @@ public final class PlaylistPlaybackTest { ...@@ -82,4 +90,43 @@ public final class PlaylistPlaybackTest {
DumpFileAsserts.assertOutput( DumpFileAsserts.assertOutput(
applicationContext, playbackOutput, "playbackdumps/playlists/bypass-off-then-on.dump"); 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");
}
} }
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.robolectric; package com.google.android.exoplayer2.robolectric;
import static java.lang.Math.max;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
...@@ -54,7 +56,7 @@ public final class PlaybackOutput implements Dumper.Dumpable { ...@@ -54,7 +56,7 @@ public final class PlaybackOutput implements Dumper.Dumpable {
private final CapturingRenderersFactory capturingRenderersFactory; private final CapturingRenderersFactory capturingRenderersFactory;
private final List<Metadata> metadatas; private final List<Metadata> metadatas;
private final List<List<Cue>> subtitles; private final List<CueGroup> subtitles;
private final List<List<Cue>> subtitlesFromDeprecatedTextOutput; private final List<List<Cue>> subtitlesFromDeprecatedTextOutput;
private PlaybackOutput(ExoPlayer player, CapturingRenderersFactory capturingRenderersFactory) { private PlaybackOutput(ExoPlayer player, CapturingRenderersFactory capturingRenderersFactory) {
...@@ -63,8 +65,8 @@ public final class PlaybackOutput implements Dumper.Dumpable { ...@@ -63,8 +65,8 @@ public final class PlaybackOutput implements Dumper.Dumpable {
metadatas = Collections.synchronizedList(new ArrayList<>()); metadatas = Collections.synchronizedList(new ArrayList<>());
subtitles = Collections.synchronizedList(new ArrayList<>()); subtitles = Collections.synchronizedList(new ArrayList<>());
subtitlesFromDeprecatedTextOutput = Collections.synchronizedList(new ArrayList<>()); subtitlesFromDeprecatedTextOutput = Collections.synchronizedList(new ArrayList<>());
// TODO: Consider passing playback position into MetadataOutput and TextOutput. Calling // TODO: Consider passing playback position into MetadataOutput. Calling
// player.getCurrentPosition() inside onMetadata/Cues will likely be non-deterministic // player.getCurrentPosition() inside onMetadata will likely be non-deterministic
// because renderer-thread != playback-thread. // because renderer-thread != playback-thread.
player.addListener( player.addListener(
new Player.Listener() { new Player.Listener() {
...@@ -80,7 +82,7 @@ public final class PlaybackOutput implements Dumper.Dumpable { ...@@ -80,7 +82,7 @@ public final class PlaybackOutput implements Dumper.Dumpable {
@Override @Override
public void onCues(CueGroup cueGroup) { public void onCues(CueGroup cueGroup) {
subtitles.add(cueGroup.cues); subtitles.add(cueGroup);
} }
}); });
} }
...@@ -152,9 +154,9 @@ public final class PlaybackOutput implements Dumper.Dumpable { ...@@ -152,9 +154,9 @@ public final class PlaybackOutput implements Dumper.Dumpable {
} }
private void dumpSubtitles(Dumper dumper) { private void dumpSubtitles(Dumper dumper) {
if (!subtitles.equals(subtitlesFromDeprecatedTextOutput)) { if (subtitles.size() != subtitlesFromDeprecatedTextOutput.size()) {
throw new IllegalStateException( 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()) { if (subtitles.isEmpty()) {
...@@ -163,7 +165,15 @@ public final class PlaybackOutput implements Dumper.Dumpable { ...@@ -163,7 +165,15 @@ public final class PlaybackOutput implements Dumper.Dumpable {
dumper.startBlock("TextOutput"); dumper.startBlock("TextOutput");
for (int i = 0; i < subtitles.size(); i++) { for (int i = 0; i < subtitles.size(); i++) {
dumper.startBlock("Subtitle[" + 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()) { if (subtitle.isEmpty()) {
dumper.add("Cues", ImmutableList.of()); dumper.add("Cues", ImmutableList.of());
} }
......
...@@ -348,8 +348,10 @@ MediaCodecAdapter (exotest.video.avc): ...@@ -348,8 +348,10 @@ MediaCodecAdapter (exotest.video.avc):
buffers[125] = length 0, hash 1 buffers[125] = length 0, hash 1
TextOutput: TextOutput:
Subtitle[0]: Subtitle[0]:
presentationTimeUs = 0
Cues = [] Cues = []
Subtitle[1]: Subtitle[1]:
presentationTimeUs = 0
Cue[0]: Cue[0]:
text = This is the first subtitle. text = This is the first subtitle.
textAlignment = ALIGN_CENTER textAlignment = ALIGN_CENTER
...@@ -360,8 +362,10 @@ TextOutput: ...@@ -360,8 +362,10 @@ TextOutput:
positionAnchor = 1 positionAnchor = 1
size = 1.0 size = 1.0
Subtitle[2]: Subtitle[2]:
presentationTimeUs = 1234000
Cues = [] Cues = []
Subtitle[3]: Subtitle[3]:
presentationTimeUs = 2345000
Cue[0]: Cue[0]:
text = This is the second subtitle. text = This is the second subtitle.
textAlignment = ALIGN_CENTER textAlignment = ALIGN_CENTER
...@@ -372,4 +376,5 @@ TextOutput: ...@@ -372,4 +376,5 @@ TextOutput:
positionAnchor = 1 positionAnchor = 1
size = 1.0 size = 1.0
Subtitle[4]: Subtitle[4]:
presentationTimeUs = 3456000
Cues = [] Cues = []
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment