Commit 1ed5d8b5 by olly Committed by Oliver Woodman

Rollback of https://github.com/google/ExoPlayer/commit/bf5e6c7862ae418895b2b6b26fae9c28aa662ee8

*** Original commit ***

Pass startPositionUs into Renderer.replaceStream

Plumb this down into BaseRenderer.onStreamChanged and use it when
deciding whether to render the first frame of a new period.

***

PiperOrigin-RevId: 323447253
parent 684994fe
Showing with 141 additions and 124 deletions
......@@ -97,8 +97,6 @@
([#7590](https://github.com/google/ExoPlayer/issues/7590)).
* Remove `AdaptiveTrackSelection.minTimeBetweenBufferReevaluationMs`
parameter ([#7582](https://github.com/google/ExoPlayer/issues/7582)).
* Distinguish between `offsetUs` and `startPositionUs` when passing new
`SampleStreams` to `Renderers`.
* Fix wrong `MediaPeriodId` for some renderer errors reported by
`AnalyticsListener.onPlayerError`.
* Remove onMediaPeriodCreated/Released/ReadingStarted from
......
......@@ -38,7 +38,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
@Nullable private SampleStream stream;
@Nullable private Format[] streamFormats;
private long streamOffsetUs;
private long lastResetPositionUs;
private long startPositionUs;
private long readingPositionUs;
private boolean streamIsFinal;
private boolean throwRendererExceptionIsExecuting;
......@@ -87,15 +87,14 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
long positionUs,
boolean joining,
boolean mayRenderStartOfStream,
long startPositionUs,
long offsetUs)
throws ExoPlaybackException {
Assertions.checkState(state == STATE_DISABLED);
this.configuration = configuration;
state = STATE_ENABLED;
lastResetPositionUs = positionUs;
startPositionUs = positionUs;
onEnabled(joining, mayRenderStartOfStream);
replaceStream(formats, stream, startPositionUs, offsetUs);
replaceStream(formats, stream, offsetUs);
onPositionReset(positionUs, joining);
}
......@@ -107,15 +106,14 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
}
@Override
public final void replaceStream(
Format[] formats, SampleStream stream, long startPositionUs, long offsetUs)
public final void replaceStream(Format[] formats, SampleStream stream, long offsetUs)
throws ExoPlaybackException {
Assertions.checkState(!streamIsFinal);
this.stream = stream;
readingPositionUs = startPositionUs;
readingPositionUs = offsetUs;
streamFormats = formats;
streamOffsetUs = offsetUs;
onStreamChanged(formats, startPositionUs, offsetUs);
onStreamChanged(formats, offsetUs);
}
@Override
......@@ -152,7 +150,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
@Override
public final void resetPosition(long positionUs) throws ExoPlaybackException {
streamIsFinal = false;
lastResetPositionUs = positionUs;
startPositionUs = positionUs;
readingPositionUs = positionUs;
onPositionReset(positionUs, false);
}
......@@ -222,21 +220,19 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* <p>The default implementation is a no-op.
*
* @param formats The enabled formats.
* @param startPositionUs The start position of the new stream in renderer time (microseconds).
* @param offsetUs The offset that will be added to the timestamps of buffers read via {@link
* #readSource(FormatHolder, DecoderInputBuffer, boolean)} so that decoder input buffers have
* monotonically increasing timestamps.
* @throws ExoPlaybackException If an error occurs.
*/
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
throws ExoPlaybackException {
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
// Do nothing.
}
/**
* Called when the position is reset. This occurs when the renderer is enabled after {@link
* #onStreamChanged(Format[], long, long)} has been called, and also when a position discontinuity
* is encountered.
* #onStreamChanged(Format[], long)} has been called, and also when a position discontinuity is
* encountered.
*
* <p>After a position reset, the renderer's {@link SampleStream} is guaranteed to provide samples
* starting from a key frame.
......@@ -295,8 +291,8 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* Returns the position passed to the most recent call to {@link #enable} or {@link
* #resetPosition}.
*/
protected final long getLastResetPositionUs() {
return lastResetPositionUs;
protected final long getStartPositionUs() {
return startPositionUs;
}
/** Returns a clear {@link FormatHolder}. */
......
......@@ -1844,10 +1844,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
// The renderer stream is not final, so we can replace the sample streams immediately.
Format[] formats = getFormats(newTrackSelectorResult.selections.get(i));
renderer.replaceStream(
formats,
readingPeriodHolder.sampleStreams[i],
readingPeriodHolder.getStartPositionRendererTime(),
readingPeriodHolder.getRendererOffset());
formats, readingPeriodHolder.sampleStreams[i], readingPeriodHolder.getRendererOffset());
} else if (renderer.isEnded()) {
// The renderer has finished playback, so we can disable it now.
disableRenderer(renderer);
......@@ -2103,7 +2100,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
rendererPositionUs,
joining,
mayRenderStartOfStream,
periodHolder.getStartPositionRendererTime(),
periodHolder.getRendererOffset());
renderer.handleMessage(
......
......@@ -68,14 +68,13 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
long positionUs,
boolean joining,
boolean mayRenderStartOfStream,
long startPositionUs,
long offsetUs)
throws ExoPlaybackException {
Assertions.checkState(state == STATE_DISABLED);
this.configuration = configuration;
state = STATE_ENABLED;
onEnabled(joining);
replaceStream(formats, stream, startPositionUs, offsetUs);
replaceStream(formats, stream, offsetUs);
onPositionReset(positionUs, joining);
}
......@@ -87,8 +86,7 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
}
@Override
public final void replaceStream(
Format[] formats, SampleStream stream, long startPositionUs, long offsetUs)
public final void replaceStream(Format[] formats, SampleStream stream, long offsetUs)
throws ExoPlaybackException {
Assertions.checkState(!streamIsFinal);
this.stream = stream;
......
......@@ -292,7 +292,6 @@ public interface Renderer extends PlayerMessage.Target {
* @param joining Whether this renderer is being enabled to join an ongoing playback.
* @param mayRenderStartOfStream Whether this renderer is allowed to render the start of the
* stream even if the state is not {@link #STATE_STARTED} yet.
* @param startPositionUs The start position of the stream in renderer time (microseconds).
* @param offsetUs The offset to be added to timestamps of buffers read from {@code stream} before
* they are rendered.
* @throws ExoPlaybackException If an error occurs.
......@@ -304,7 +303,6 @@ public interface Renderer extends PlayerMessage.Target {
long positionUs,
boolean joining,
boolean mayRenderStartOfStream,
long startPositionUs,
long offsetUs)
throws ExoPlaybackException;
......@@ -327,12 +325,11 @@ public interface Renderer extends PlayerMessage.Target {
*
* @param formats The enabled formats.
* @param stream The {@link SampleStream} from which the renderer should consume.
* @param startPositionUs The start position of the new stream in renderer time (microseconds).
* @param offsetUs The offset to be added to timestamps of buffers read from {@code stream} before
* they are rendered.
* @throws ExoPlaybackException If an error occurs.
*/
void replaceStream(Format[] formats, SampleStream stream, long startPositionUs, long offsetUs)
void replaceStream(Format[] formats, SampleStream stream, long offsetUs)
throws ExoPlaybackException;
/** Returns the {@link SampleStream} being consumed, or null if the renderer is disabled. */
......@@ -348,7 +345,7 @@ public interface Renderer extends PlayerMessage.Target {
boolean hasReadStreamToEnd();
/**
* Returns the renderer time up to which the renderer has read samples from the current {@link
* Returns the playback position up to which the renderer has read samples from the current {@link
* SampleStream}, in microseconds, or {@link C#TIME_END_OF_SOURCE} if the renderer has read the
* current {@link SampleStream} to the end.
*
......@@ -421,8 +418,8 @@ public interface Renderer extends PlayerMessage.Target {
* <p>The renderer may also render the very start of the media at the current position (e.g. the
* first frame of a video stream) while still in the {@link #STATE_ENABLED} state, unless it's the
* initial start of the media after calling {@link #enable(RendererConfiguration, Format[],
* SampleStream, long, boolean, boolean, long, long)} with {@code mayRenderStartOfStream} set to
* {@code false}.
* SampleStream, long, boolean, boolean, long)} with {@code mayRenderStartOfStream} set to {@code
* false}.
*
* <p>This method should return quickly, and should not block if the renderer is unable to make
* useful progress.
......
......@@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.mediacodec;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import static java.lang.Math.max;
import android.annotation.TargetApi;
......@@ -353,7 +352,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private final TimedValueQueue<Format> formatQueue;
private final ArrayList<Long> decodeOnlyPresentationTimestamps;
private final MediaCodec.BufferInfo outputBufferInfo;
private final long[] pendingOutputStreamStartPositionsUs;
private final long[] pendingOutputStreamOffsetsUs;
private final long[] pendingOutputStreamSwitchTimesUs;
......@@ -409,7 +407,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@MediaCodecOperationMode private int mediaCodecOperationMode;
@Nullable private ExoPlaybackException pendingPlaybackException;
protected DecoderCounters decoderCounters;
private long outputStreamStartPositionUs;
private long outputStreamOffsetUs;
private int pendingOutputStreamOffsetCount;
private boolean receivedOutputMediaFormatChange;
......@@ -441,10 +438,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
outputBufferInfo = new MediaCodec.BufferInfo();
operatingRate = 1f;
mediaCodecOperationMode = OPERATION_MODE_SYNCHRONOUS;
pendingOutputStreamStartPositionsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
outputStreamStartPositionUs = C.TIME_UNSET;
outputStreamOffsetUs = C.TIME_UNSET;
bypassBatchBuffer = new BatchBuffer();
resetCodecStateForRelease();
......@@ -667,12 +662,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
throws ExoPlaybackException {
if (this.outputStreamOffsetUs == C.TIME_UNSET) {
checkState(this.outputStreamStartPositionUs == C.TIME_UNSET);
this.outputStreamStartPositionUs = startPositionUs;
this.outputStreamOffsetUs = offsetUs;
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
if (outputStreamOffsetUs == C.TIME_UNSET) {
outputStreamOffsetUs = offsetUs;
} else {
if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) {
Log.w(
......@@ -682,7 +674,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} else {
pendingOutputStreamOffsetCount++;
}
pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1] = startPositionUs;
pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs;
pendingOutputStreamSwitchTimesUs[pendingOutputStreamOffsetCount - 1] =
largestQueuedPresentationTimeUs;
......@@ -708,8 +699,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
formatQueue.clear();
if (pendingOutputStreamOffsetCount != 0) {
outputStreamOffsetUs = pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1];
outputStreamStartPositionUs =
pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1];
pendingOutputStreamOffsetCount = 0;
}
}
......@@ -727,7 +716,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@Override
protected void onDisabled() {
inputFormat = null;
outputStreamStartPositionUs = C.TIME_UNSET;
outputStreamOffsetUs = C.TIME_UNSET;
pendingOutputStreamOffsetCount = 0;
if (sourceDrmSession != null || codecDrmSession != null) {
......@@ -1553,16 +1541,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
protected void onProcessedOutputBuffer(long presentationTimeUs) {
while (pendingOutputStreamOffsetCount != 0
&& presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) {
outputStreamStartPositionUs = pendingOutputStreamStartPositionsUs[0];
outputStreamOffsetUs = pendingOutputStreamOffsetsUs[0];
pendingOutputStreamOffsetCount--;
System.arraycopy(
pendingOutputStreamStartPositionsUs,
/* srcPos= */ 1,
pendingOutputStreamStartPositionsUs,
/* destPos= */ 0,
pendingOutputStreamOffsetCount);
System.arraycopy(
pendingOutputStreamOffsetsUs,
/* srcPos= */ 1,
pendingOutputStreamOffsetsUs,
......@@ -1955,13 +1936,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
/**
* Returns the start position of the output {@link SampleStream}, in renderer time microseconds.
*/
protected final long getOutputStreamStartPositionUs() {
return outputStreamStartPositionUs;
}
/**
* Returns the offset that should be subtracted from {@code bufferPresentationTimeUs} in {@link
* #processOutputBuffer(long, long, MediaCodec, ByteBuffer, int, int, int, long, boolean, boolean,
* Format)} to get the playback position with respect to the media.
......@@ -2096,7 +2070,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
BatchBuffer batchBuffer = bypassBatchBuffer;
// Let's process the pending buffer if any.
checkState(!outputStreamEnded);
Assertions.checkState(!outputStreamEnded);
if (!batchBuffer.isEmpty()) { // Optimisation: Do not process buffer if empty.
if (processOutputBuffer(
positionUs,
......@@ -2135,7 +2109,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
// Now refill the empty buffer for the next iteration.
checkState(!inputStreamEnded);
Assertions.checkState(!inputStreamEnded);
FormatHolder formatHolder = getFormatHolder();
boolean formatChange = readBatchFromSource(formatHolder, batchBuffer);
......
......@@ -110,7 +110,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
}
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
protected void onStreamChanged(Format[] formats, long offsetUs) {
decoder = decoderFactory.createDecoder(formats[0]);
}
......
......@@ -142,7 +142,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
}
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
protected void onStreamChanged(Format[] formats, long offsetUs) {
streamFormat = formats[0];
if (decoder != null) {
decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM;
......
......@@ -302,13 +302,12 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
}
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
throws ExoPlaybackException {
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
// TODO: This shouldn't just update the output stream offset as long as there are still buffers
// of the previous stream in the decoder. It should also make sure to render the first frame of
// the next stream if the playback position reached the new stream.
outputStreamOffsetUs = offsetUs;
super.onStreamChanged(formats, startPositionUs, offsetUs);
super.onStreamChanged(formats, offsetUs);
}
/**
......
......@@ -777,7 +777,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
initialPositionUs = positionUs;
}
long presentationTimeUs = bufferPresentationTimeUs - getOutputStreamOffsetUs();
long outputStreamOffsetUs = getOutputStreamOffsetUs();
long presentationTimeUs = bufferPresentationTimeUs - outputStreamOffsetUs;
if (isDecodeOnlyBuffer && !isLastBuffer) {
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
......@@ -805,7 +806,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// Don't force output until we joined and the position reached the current stream.
boolean forceRenderOutputBuffer =
joiningDeadlineMs == C.TIME_UNSET
&& positionUs >= getOutputStreamStartPositionUs()
&& positionUs >= outputStreamOffsetUs
&& (shouldRenderFirstFrame
|| (isStarted && shouldForceRenderOutputBuffer(earlyUs, elapsedSinceLastRenderUs)));
if (forceRenderOutputBuffer) {
......
......@@ -73,7 +73,7 @@ public final class CameraMotionRenderer extends BaseRenderer {
}
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
protected void onStreamChanged(Format[] formats, long offsetUs) {
this.offsetUs = offsetUs;
}
......
......@@ -7566,7 +7566,7 @@ public final class ExoPlayerTest {
boolean pendingFirstBufferTime = false;
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
protected void onStreamChanged(Format[] formats, long offsetUs) {
rendererStreamOffsetsUs.add(offsetUs);
pendingFirstBufferTime = true;
}
......
......@@ -1570,8 +1570,7 @@ public final class AnalyticsCollectorTest {
private int streamChangeCount = 0;
@Override
protected void onStreamChanged(
Format[] formats, long startPositionUs, long offsetUs)
protected void onStreamChanged(Format[] formats, long offsetUs)
throws ExoPlaybackException {
// Fail when changing streams for the second time. This will happen during the
// period transition (as the first time is when enabling the stream initially).
......
......@@ -116,7 +116,6 @@ public class DecoderAudioRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs= */ 0);
audioRenderer.setCurrentStreamFinal();
when(mockAudioSink.isEnded()).thenReturn(true);
......
......@@ -127,7 +127,6 @@ public class MediaCodecAudioRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecAudioRenderer.start();
......@@ -182,7 +181,6 @@ public class MediaCodecAudioRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecAudioRenderer.start();
......@@ -250,7 +248,6 @@ public class MediaCodecAudioRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
exceptionThrowingRenderer.start();
......
......@@ -154,7 +154,6 @@ public class MetadataRendererTest {
ImmutableList.of(
FakeSampleStreamItem.sample(/* timeUs= */ 0, /* flags= */ 0, input),
FakeSampleStreamItem.END_OF_STREAM_ITEM)),
/* startPositionUs= */ 0L,
/* offsetUs= */ 0L);
renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the format
renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the data
......
......@@ -197,7 +197,6 @@ public final class DecoderVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0L,
/* offsetUs */ 0);
for (int i = 0; i < 10; i++) {
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
......@@ -226,7 +225,6 @@ public final class DecoderVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
for (int i = 0; i < 10; i++) {
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
......@@ -254,7 +252,6 @@ public final class DecoderVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
renderer.start();
for (int i = 0; i < 10; i++) {
......@@ -269,7 +266,7 @@ public final class DecoderVideoRendererTest {
// TODO: Fix rendering of first frame at stream transition.
@Ignore
@Test
public void replaceStream_rendersFirstFrameOnlyAfterStartPosition() throws Exception {
public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception {
FakeSampleStream fakeSampleStream1 =
new FakeSampleStream(
/* mediaSourceEventDispatcher= */ null,
......@@ -285,7 +282,7 @@ public final class DecoderVideoRendererTest {
new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT,
ImmutableList.of(
oneByteSample(/* timeUs= */ 1_000_000), FakeSampleStreamItem.END_OF_STREAM_ITEM));
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM));
renderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {H264_FORMAT},
......@@ -293,31 +290,67 @@ public final class DecoderVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
renderer.start();
boolean replacedStream = false;
// Render until just before the start position of the second stream
for (int i = 0; i < 5; i++) {
for (int i = 0; i <= 10; i++) {
renderer.render(/* positionUs= */ i * 10, SystemClock.elapsedRealtime() * 1000);
if (!replacedStream && renderer.hasReadStreamToEnd()) {
renderer.replaceStream(new Format[] {H264_FORMAT}, fakeSampleStream2, /* offsetUs= */ 100);
replacedStream = true;
}
// Ensure pending messages are delivered.
ShadowLooper.idleMainLooper();
}
verify(eventListener, times(2)).onRenderedFirstFrame(any());
}
// TODO: Fix rendering of first frame at stream transition.
@Ignore
@Test
public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception {
FakeSampleStream fakeSampleStream1 =
new FakeSampleStream(
/* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT,
ImmutableList.of(
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM));
FakeSampleStream fakeSampleStream2 =
new FakeSampleStream(
/* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT,
ImmutableList.of(
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM));
renderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {H264_FORMAT},
fakeSampleStream1,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
boolean replacedStream = false;
for (int i = 0; i < 10; i++) {
renderer.render(/* positionUs= */ i * 10, SystemClock.elapsedRealtime() * 1000);
if (!replacedStream && renderer.hasReadStreamToEnd()) {
renderer.replaceStream(
new Format[] {H264_FORMAT},
fakeSampleStream2,
/* startPositionUs= */ 50,
/* offsetUs= */ 100);
renderer.replaceStream(new Format[] {H264_FORMAT}, fakeSampleStream2, /* offsetUs= */ 100);
replacedStream = true;
}
// Ensure pending messages are delivered.
ShadowLooper.idleMainLooper();
}
// Expect only the first frame of the first stream to have been rendered.
verify(eventListener).onRenderedFirstFrame(any());
// Render to the start position of the stream and verify the new first frame gets rendered (even
// though its sampleTimeUs is far in the future).
renderer.render(/* positionUs= */ 50, SystemClock.elapsedRealtime() * 1000);
// Render to streamOffsetUs and verify the new first frame gets rendered.
renderer.render(/* positionUs= */ 100, SystemClock.elapsedRealtime() * 1000);
verify(eventListener, times(2)).onRenderedFirstFrame(any());
}
......
......@@ -143,7 +143,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
......@@ -176,7 +175,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.setCurrentStreamFinal();
mediaCodecVideoRenderer.start();
......@@ -219,7 +217,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
......@@ -265,7 +262,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
......@@ -302,7 +298,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
for (int i = 0; i < 10; i++) {
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
......@@ -330,7 +325,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
for (int i = 0; i < 10; i++) {
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
......@@ -357,7 +351,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
for (int i = 0; i < 10; i++) {
......@@ -387,7 +380,7 @@ public class MediaCodecVideoRendererTest {
new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264,
ImmutableList.of(
oneByteSample(/* timeUs= */ 1_000_000, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
......@@ -396,33 +389,73 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
boolean replacedStream = false;
// Render until just before the start position of the second stream
for (int i = 0; i < 5; i++) {
for (int i = 0; i <= 10; i++) {
mediaCodecVideoRenderer.render(
/* positionUs= */ i * 10, SystemClock.elapsedRealtime() * 1000);
if (!replacedStream && mediaCodecVideoRenderer.hasReadStreamToEnd()) {
mediaCodecVideoRenderer.replaceStream(
new Format[] {VIDEO_H264},
fakeSampleStream2,
/* startPositionUs= */ 50,
/* offsetUs= */ 100);
new Format[] {VIDEO_H264}, fakeSampleStream2, /* offsetUs= */ 100);
replacedStream = true;
}
}
// Expect only the first frame of the first stream to have been rendered.
shadowLooper.idle();
verify(eventListener).onRenderedFirstFrame(any());
verify(eventListener, times(2)).onRenderedFirstFrame(any());
}
@Test
public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception {
ShadowLooper shadowLooper = shadowOf(testMainLooper);
FakeSampleStream fakeSampleStream1 =
new FakeSampleStream(
/* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264,
ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM));
FakeSampleStream fakeSampleStream2 =
new FakeSampleStream(
/* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264,
ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream1,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
boolean replacedStream = false;
for (int i = 0; i < 10; i++) {
mediaCodecVideoRenderer.render(
/* positionUs= */ i * 10, SystemClock.elapsedRealtime() * 1000);
if (!replacedStream && mediaCodecVideoRenderer.hasReadStreamToEnd()) {
mediaCodecVideoRenderer.replaceStream(
new Format[] {VIDEO_H264}, fakeSampleStream2, /* offsetUs= */ 100);
replacedStream = true;
}
}
// Render to the start position of the stream and verify the new first frame gets rendered (even
// though its sampleTimeUs is far in the future).
mediaCodecVideoRenderer.render(/* positionUs= */ 50, SystemClock.elapsedRealtime() * 1000);
shadowLooper.idle();
verify(eventListener).onRenderedFirstFrame(any());
// Render to streamOffsetUs and verify the new first frame gets rendered.
mediaCodecVideoRenderer.render(/* positionUs= */ 100, SystemClock.elapsedRealtime() * 1000);
shadowLooper.idle();
verify(eventListener, times(2)).onRenderedFirstFrame(any());
}
......@@ -456,7 +489,6 @@ public class MediaCodecVideoRendererTest {
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.setCurrentStreamFinal();
......
......@@ -33,7 +33,7 @@ public class FakeVideoRenderer extends FakeRenderer {
private final VideoRendererEventListener.EventDispatcher eventDispatcher;
private final DecoderCounters decoderCounters;
private @MonotonicNonNull Format format;
private long startPositionUs;
private long streamOffsetUs;
private boolean renderedFirstFrameAfterReset;
private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
private boolean renderedFirstFrameAfterEnable;
......@@ -54,10 +54,9 @@ public class FakeVideoRenderer extends FakeRenderer {
}
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
throws ExoPlaybackException {
super.onStreamChanged(formats, startPositionUs, offsetUs);
this.startPositionUs = startPositionUs;
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
super.onStreamChanged(formats, offsetUs);
streamOffsetUs = offsetUs;
if (renderedFirstFrameAfterReset) {
renderedFirstFrameAfterReset = false;
}
......@@ -102,7 +101,7 @@ public class FakeVideoRenderer extends FakeRenderer {
!renderedFirstFrameAfterEnable
? (getState() == Renderer.STATE_STARTED || mayRenderFirstFrameAfterEnableIfNotStarted)
: !renderedFirstFrameAfterReset;
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= startPositionUs;
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= streamOffsetUs;
if (shouldProcess && !renderedFirstFrameAfterReset) {
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format);
eventDispatcher.videoSizeChanged(
......
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