mirror of
https://github.com/samsonjs/media.git
synced 2026-04-17 13:15:47 +00:00
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: 321175627
This commit is contained in:
parent
5c4b8085a0
commit
bf5e6c7862
18 changed files with 133 additions and 147 deletions
|
|
@ -96,6 +96,8 @@
|
|||
([#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`.
|
||||
* Video: Pass frame rate hint to `Surface.setFrameRate` on Android R devices.
|
||||
* Track selection:
|
||||
* Add `Player.getTrackSelector`.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
|
|||
private SampleStream stream;
|
||||
private Format[] streamFormats;
|
||||
private long streamOffsetUs;
|
||||
private long startPositionUs;
|
||||
private long lastResetPositionUs;
|
||||
private long readingPositionUs;
|
||||
private boolean streamIsFinal;
|
||||
private boolean throwRendererExceptionIsExecuting;
|
||||
|
|
@ -85,14 +85,15 @@ 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;
|
||||
startPositionUs = positionUs;
|
||||
lastResetPositionUs = positionUs;
|
||||
onEnabled(joining, mayRenderStartOfStream);
|
||||
replaceStream(formats, stream, offsetUs);
|
||||
replaceStream(formats, stream, startPositionUs, offsetUs);
|
||||
onPositionReset(positionUs, joining);
|
||||
}
|
||||
|
||||
|
|
@ -104,14 +105,15 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
|
|||
}
|
||||
|
||||
@Override
|
||||
public final void replaceStream(Format[] formats, SampleStream stream, long offsetUs)
|
||||
public final void replaceStream(
|
||||
Format[] formats, SampleStream stream, long startPositionUs, long offsetUs)
|
||||
throws ExoPlaybackException {
|
||||
Assertions.checkState(!streamIsFinal);
|
||||
this.stream = stream;
|
||||
readingPositionUs = offsetUs;
|
||||
readingPositionUs = startPositionUs;
|
||||
streamFormats = formats;
|
||||
streamOffsetUs = offsetUs;
|
||||
onStreamChanged(formats, offsetUs);
|
||||
onStreamChanged(formats, startPositionUs, offsetUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -148,7 +150,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
|
|||
@Override
|
||||
public final void resetPosition(long positionUs) throws ExoPlaybackException {
|
||||
streamIsFinal = false;
|
||||
startPositionUs = positionUs;
|
||||
lastResetPositionUs = positionUs;
|
||||
readingPositionUs = positionUs;
|
||||
onPositionReset(positionUs, false);
|
||||
}
|
||||
|
|
@ -218,24 +220,26 @@ 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 offsetUs) throws ExoPlaybackException {
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
|
||||
throws ExoPlaybackException {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the position is reset. This occurs when the renderer is enabled after
|
||||
* {@link #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
|
||||
* 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.
|
||||
*
|
||||
* <p>After a position reset, the renderer's {@link SampleStream} is guaranteed to provide samples
|
||||
* starting from a key frame.
|
||||
* <p>
|
||||
* The default implementation is a no-op.
|
||||
*
|
||||
* <p>The default implementation is a no-op.
|
||||
*
|
||||
* @param positionUs The new playback position in microseconds.
|
||||
* @param joining Whether this renderer is being enabled to join an ongoing playback.
|
||||
|
|
@ -289,8 +293,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 getStartPositionUs() {
|
||||
return startPositionUs;
|
||||
protected final long getLastResetPositionUs() {
|
||||
return lastResetPositionUs;
|
||||
}
|
||||
|
||||
/** Returns a clear {@link FormatHolder}. */
|
||||
|
|
|
|||
|
|
@ -1873,7 +1873,10 @@ 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.getRendererOffset());
|
||||
formats,
|
||||
readingPeriodHolder.sampleStreams[i],
|
||||
readingPeriodHolder.getStartPositionRendererTime(),
|
||||
readingPeriodHolder.getRendererOffset());
|
||||
} else if (renderer.isEnded()) {
|
||||
// The renderer has finished playback, so we can disable it now.
|
||||
disableRenderer(renderer);
|
||||
|
|
@ -2128,6 +2131,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
rendererPositionUs,
|
||||
joining,
|
||||
mayRenderStartOfStream,
|
||||
periodHolder.getStartPositionRendererTime(),
|
||||
periodHolder.getRendererOffset());
|
||||
|
||||
renderer.handleMessage(
|
||||
|
|
|
|||
|
|
@ -68,13 +68,14 @@ 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, offsetUs);
|
||||
replaceStream(formats, stream, startPositionUs, offsetUs);
|
||||
onPositionReset(positionUs, joining);
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +87,8 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
|
|||
}
|
||||
|
||||
@Override
|
||||
public final void replaceStream(Format[] formats, SampleStream stream, long offsetUs)
|
||||
public final void replaceStream(
|
||||
Format[] formats, SampleStream stream, long startPositionUs, long offsetUs)
|
||||
throws ExoPlaybackException {
|
||||
Assertions.checkState(!streamIsFinal);
|
||||
this.stream = stream;
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ 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.
|
||||
|
|
@ -303,6 +304,7 @@ public interface Renderer extends PlayerMessage.Target {
|
|||
long positionUs,
|
||||
boolean joining,
|
||||
boolean mayRenderStartOfStream,
|
||||
long startPositionUs,
|
||||
long offsetUs)
|
||||
throws ExoPlaybackException;
|
||||
|
||||
|
|
@ -319,17 +321,18 @@ public interface Renderer extends PlayerMessage.Target {
|
|||
|
||||
/**
|
||||
* Replaces the {@link SampleStream} from which samples will be consumed.
|
||||
* <p>
|
||||
* This method may be called when the renderer is in the following states:
|
||||
* {@link #STATE_ENABLED}, {@link #STATE_STARTED}.
|
||||
*
|
||||
* <p>This method may be called when the renderer is in the following states: {@link
|
||||
* #STATE_ENABLED}, {@link #STATE_STARTED}.
|
||||
*
|
||||
* @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 offsetUs)
|
||||
void replaceStream(Format[] formats, SampleStream stream, long startPositionUs, long offsetUs)
|
||||
throws ExoPlaybackException;
|
||||
|
||||
/** Returns the {@link SampleStream} being consumed, or null if the renderer is disabled. */
|
||||
|
|
@ -345,7 +348,7 @@ public interface Renderer extends PlayerMessage.Target {
|
|||
boolean hasReadStreamToEnd();
|
||||
|
||||
/**
|
||||
* Returns the playback position up to which the renderer has read samples from the current {@link
|
||||
* Returns the renderer time 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.
|
||||
*
|
||||
|
|
@ -418,8 +421,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)} with {@code mayRenderStartOfStream} set to {@code
|
||||
* false}.
|
||||
* SampleStream, long, boolean, boolean, long, 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,6 +15,8 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.mediacodec;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodec.CodecException;
|
||||
|
|
@ -350,6 +352,7 @@ 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;
|
||||
|
||||
|
|
@ -406,6 +409,7 @@ 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;
|
||||
|
|
@ -438,8 +442,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
operatingRate = 1f;
|
||||
renderTimeLimitMs = C.TIME_UNSET;
|
||||
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();
|
||||
|
|
@ -676,9 +682,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
|
||||
if (outputStreamOffsetUs == C.TIME_UNSET) {
|
||||
outputStreamOffsetUs = offsetUs;
|
||||
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;
|
||||
} else {
|
||||
if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) {
|
||||
Log.w(
|
||||
|
|
@ -688,6 +697,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
} else {
|
||||
pendingOutputStreamOffsetCount++;
|
||||
}
|
||||
pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1] = startPositionUs;
|
||||
pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs;
|
||||
pendingOutputStreamSwitchTimesUs[pendingOutputStreamOffsetCount - 1] =
|
||||
largestQueuedPresentationTimeUs;
|
||||
|
|
@ -713,6 +723,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
formatQueue.clear();
|
||||
if (pendingOutputStreamOffsetCount != 0) {
|
||||
outputStreamOffsetUs = pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1];
|
||||
outputStreamStartPositionUs =
|
||||
pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1];
|
||||
pendingOutputStreamOffsetCount = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -730,6 +742,7 @@ 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) {
|
||||
|
|
@ -1562,8 +1575,15 @@ 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,
|
||||
|
|
@ -1953,6 +1973,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
return largestQueuedPresentationTimeUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
|
@ -2088,7 +2115,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
BatchBuffer batchBuffer = bypassBatchBuffer;
|
||||
|
||||
// Let's process the pending buffer if any.
|
||||
Assertions.checkState(!outputStreamEnded);
|
||||
checkState(!outputStreamEnded);
|
||||
if (!batchBuffer.isEmpty()) { // Optimisation: Do not process buffer if empty.
|
||||
if (processOutputBuffer(
|
||||
positionUs,
|
||||
|
|
@ -2127,7 +2154,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
// Now refill the empty buffer for the next iteration.
|
||||
Assertions.checkState(!inputStreamEnded);
|
||||
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 offsetUs) {
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
|
||||
decoder = decoderFactory.createDecoder(formats[0]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats, long offsetUs) {
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
|
||||
streamFormat = formats[0];
|
||||
if (decoder != null) {
|
||||
decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM;
|
||||
|
|
|
|||
|
|
@ -300,12 +300,13 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, 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, offsetUs);
|
||||
super.onStreamChanged(formats, startPositionUs, offsetUs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -774,8 +774,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
initialPositionUs = positionUs;
|
||||
}
|
||||
|
||||
long outputStreamOffsetUs = getOutputStreamOffsetUs();
|
||||
long presentationTimeUs = bufferPresentationTimeUs - outputStreamOffsetUs;
|
||||
long presentationTimeUs = bufferPresentationTimeUs - getOutputStreamOffsetUs();
|
||||
|
||||
if (isDecodeOnlyBuffer && !isLastBuffer) {
|
||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||
|
|
@ -803,7 +802,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 >= outputStreamOffsetUs
|
||||
&& positionUs >= getOutputStreamStartPositionUs()
|
||||
&& (shouldRenderFirstFrame
|
||||
|| (isStarted && shouldForceRenderOutputBuffer(earlyUs, elapsedSinceLastRenderUs)));
|
||||
if (forceRenderOutputBuffer) {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public final class CameraMotionRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats, long offsetUs) {
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
|
||||
this.offsetUs = offsetUs;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7245,7 +7245,7 @@ public final class ExoPlayerTest {
|
|||
FakeRenderer audioRenderer =
|
||||
new FakeRenderer(C.TRACK_TYPE_AUDIO) {
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats, long offsetUs)
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
|
||||
throws ExoPlaybackException {
|
||||
// Fail when changing streams. This will happen during the period transition.
|
||||
throw createRendererException(
|
||||
|
|
@ -7758,7 +7758,7 @@ public final class ExoPlayerTest {
|
|||
boolean pendingFirstBufferTime = false;
|
||||
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats, long offsetUs) {
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
|
||||
rendererStreamOffsetsUs.add(offsetUs);
|
||||
pendingFirstBufferTime = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ public class DecoderAudioRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ true,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs= */ 0);
|
||||
audioRenderer.setCurrentStreamFinal();
|
||||
when(mockAudioSink.isEnded()).thenReturn(true);
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ public class MediaCodecAudioRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ false,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
|
||||
mediaCodecAudioRenderer.start();
|
||||
|
|
@ -181,6 +182,7 @@ public class MediaCodecAudioRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ false,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
|
||||
mediaCodecAudioRenderer.start();
|
||||
|
|
@ -248,6 +250,7 @@ public class MediaCodecAudioRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ false,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
|
||||
exceptionThrowingRenderer.start();
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ 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
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ 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);
|
||||
|
|
@ -227,6 +228,7 @@ 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,6 +256,7 @@ public final class DecoderVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ false,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
renderer.start();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
|
|
@ -268,7 +271,7 @@ public final class DecoderVideoRendererTest {
|
|||
// TODO: Fix rendering of first frame at stream transition.
|
||||
@Ignore
|
||||
@Test
|
||||
public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception {
|
||||
public void replaceStream_rendersFirstFrameOnlyAfterStartPosition() throws Exception {
|
||||
FakeSampleStream fakeSampleStream1 =
|
||||
new FakeSampleStream(
|
||||
/* mediaSourceEventDispatcher= */ null,
|
||||
|
|
@ -284,7 +287,7 @@ public final class DecoderVideoRendererTest {
|
|||
new DrmSessionEventListener.EventDispatcher(),
|
||||
/* initialFormat= */ H264_FORMAT,
|
||||
ImmutableList.of(
|
||||
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM));
|
||||
oneByteSample(/* timeUs= */ 1_000_000), FakeSampleStreamItem.END_OF_STREAM_ITEM));
|
||||
renderer.enable(
|
||||
RendererConfiguration.DEFAULT,
|
||||
new Format[] {H264_FORMAT},
|
||||
|
|
@ -292,67 +295,31 @@ public final class DecoderVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ true,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
renderer.start();
|
||||
|
||||
boolean replacedStream = false;
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
// Render until just before the start position of the second stream
|
||||
for (int i = 0; i < 5; 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, /* offsetUs= */ 100);
|
||||
renderer.replaceStream(
|
||||
new Format[] {H264_FORMAT},
|
||||
fakeSampleStream2,
|
||||
/* startPositionUs= */ 50,
|
||||
/* 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 streamOffsetUs and verify the new first frame gets rendered.
|
||||
renderer.render(/* positionUs= */ 100, SystemClock.elapsedRealtime() * 1000);
|
||||
// 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);
|
||||
|
||||
verify(eventListener, times(2)).onRenderedFirstFrame(any());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ public class MediaCodecVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ true,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
|
||||
mediaCodecVideoRenderer.start();
|
||||
|
|
@ -171,6 +172,7 @@ public class MediaCodecVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ true,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
mediaCodecVideoRenderer.setCurrentStreamFinal();
|
||||
mediaCodecVideoRenderer.start();
|
||||
|
|
@ -212,6 +214,7 @@ public class MediaCodecVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ false,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
mediaCodecVideoRenderer.start();
|
||||
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||
|
|
@ -256,6 +259,7 @@ public class MediaCodecVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ true,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
|
||||
mediaCodecVideoRenderer.start();
|
||||
|
|
@ -291,6 +295,7 @@ 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);
|
||||
|
|
@ -317,6 +322,7 @@ 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);
|
||||
|
|
@ -342,6 +348,7 @@ public class MediaCodecVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ false,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
mediaCodecVideoRenderer.start();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
|
|
@ -352,7 +359,7 @@ public class MediaCodecVideoRendererTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception {
|
||||
public void replaceStream_rendersFirstFrameOnlyAfterStartPosition() throws Exception {
|
||||
FakeSampleStream fakeSampleStream1 =
|
||||
new FakeSampleStream(
|
||||
/* mediaSourceEventDispatcher= */ null,
|
||||
|
|
@ -369,7 +376,7 @@ public class MediaCodecVideoRendererTest {
|
|||
new DrmSessionEventListener.EventDispatcher(),
|
||||
/* initialFormat= */ VIDEO_H264,
|
||||
ImmutableList.of(
|
||||
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 1_000_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
FakeSampleStreamItem.END_OF_STREAM_ITEM));
|
||||
mediaCodecVideoRenderer.enable(
|
||||
RendererConfiguration.DEFAULT,
|
||||
|
|
@ -378,67 +385,30 @@ public class MediaCodecVideoRendererTest {
|
|||
/* positionUs= */ 0,
|
||||
/* joining= */ false,
|
||||
/* mayRenderStartOfStream= */ true,
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs */ 0);
|
||||
mediaCodecVideoRenderer.start();
|
||||
|
||||
boolean replacedStream = false;
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
// Render until just before the start position of the second stream
|
||||
for (int i = 0; i < 5; i++) {
|
||||
mediaCodecVideoRenderer.render(
|
||||
/* positionUs= */ i * 10, SystemClock.elapsedRealtime() * 1000);
|
||||
if (!replacedStream && mediaCodecVideoRenderer.hasReadStreamToEnd()) {
|
||||
mediaCodecVideoRenderer.replaceStream(
|
||||
new Format[] {VIDEO_H264}, fakeSampleStream2, /* offsetUs= */ 100);
|
||||
replacedStream = true;
|
||||
}
|
||||
}
|
||||
|
||||
verify(eventListener, times(2)).onRenderedFirstFrame(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception {
|
||||
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);
|
||||
new Format[] {VIDEO_H264},
|
||||
fakeSampleStream2,
|
||||
/* startPositionUs= */ 50,
|
||||
/* offsetUs= */ 100);
|
||||
replacedStream = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Expect only the first frame of the first stream to have been rendered.
|
||||
verify(eventListener).onRenderedFirstFrame(any());
|
||||
|
||||
// Render to streamOffsetUs and verify the new first frame gets rendered.
|
||||
mediaCodecVideoRenderer.render(/* positionUs= */ 100, SystemClock.elapsedRealtime() * 1000);
|
||||
// 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);
|
||||
|
||||
verify(eventListener, times(2)).onRenderedFirstFrame(any());
|
||||
}
|
||||
|
|
@ -473,6 +443,7 @@ 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 streamOffsetUs;
|
||||
private long startPositionUs;
|
||||
private boolean renderedFirstFrameAfterReset;
|
||||
private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
|
||||
private boolean renderedFirstFrameAfterEnable;
|
||||
|
|
@ -54,9 +54,10 @@ public class FakeVideoRenderer extends FakeRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
|
||||
super.onStreamChanged(formats, offsetUs);
|
||||
streamOffsetUs = offsetUs;
|
||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
|
||||
throws ExoPlaybackException {
|
||||
super.onStreamChanged(formats, startPositionUs, offsetUs);
|
||||
this.startPositionUs = startPositionUs;
|
||||
if (renderedFirstFrameAfterReset) {
|
||||
renderedFirstFrameAfterReset = false;
|
||||
}
|
||||
|
|
@ -101,7 +102,7 @@ public class FakeVideoRenderer extends FakeRenderer {
|
|||
!renderedFirstFrameAfterEnable
|
||||
? (getState() == Renderer.STATE_STARTED || mayRenderFirstFrameAfterEnableIfNotStarted)
|
||||
: !renderedFirstFrameAfterReset;
|
||||
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= streamOffsetUs;
|
||||
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= startPositionUs;
|
||||
if (shouldProcess && !renderedFirstFrameAfterReset) {
|
||||
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format);
|
||||
eventDispatcher.videoSizeChanged(
|
||||
|
|
|
|||
Loading…
Reference in a new issue