Add util method BaseRenderer.getStreamOffsetUs

This simplifies the common pattern of saving this offset in
onStreamChanged to use it at a later point.

PiperOrigin-RevId: 666226263
This commit is contained in:
tonihei 2024-08-22 00:49:42 -07:00 committed by Copybara-Service
parent 4ba9f59492
commit 6f0cb539d0
8 changed files with 20 additions and 35 deletions

View File

@ -418,6 +418,15 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
return lastResetPositionUs; return lastResetPositionUs;
} }
/**
* Returns the offset added to timestamps of buffers read from the {@link SampleStream}.
*
* <p>Must only be called if the renderer is at least {@link #STATE_ENABLED}.
*/
protected final long getStreamOffsetUs() {
return streamOffsetUs;
}
/** Returns a clear {@link FormatHolder}. */ /** Returns a clear {@link FormatHolder}. */
protected final FormatHolder getFormatHolder() { protected final FormatHolder getFormatHolder() {
formatHolder.clear(); formatHolder.clear();

View File

@ -119,7 +119,6 @@ public final class TextRenderer extends BaseRenderer implements Callback {
private boolean inputStreamEnded; private boolean inputStreamEnded;
private boolean outputStreamEnded; private boolean outputStreamEnded;
@Nullable private Format streamFormat; @Nullable private Format streamFormat;
private long outputStreamOffsetUs;
private long lastRendererPositionUs; private long lastRendererPositionUs;
private long finalStreamEndPositionUs; private long finalStreamEndPositionUs;
private boolean legacyDecodingEnabled; private boolean legacyDecodingEnabled;
@ -159,7 +158,6 @@ public final class TextRenderer extends BaseRenderer implements Callback {
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
finalStreamEndPositionUs = C.TIME_UNSET; finalStreamEndPositionUs = C.TIME_UNSET;
outputStreamOffsetUs = C.TIME_UNSET;
lastRendererPositionUs = C.TIME_UNSET; lastRendererPositionUs = C.TIME_UNSET;
legacyDecodingEnabled = false; legacyDecodingEnabled = false;
} }
@ -206,7 +204,6 @@ public final class TextRenderer extends BaseRenderer implements Callback {
long startPositionUs, long startPositionUs,
long offsetUs, long offsetUs,
MediaSource.MediaPeriodId mediaPeriodId) { MediaSource.MediaPeriodId mediaPeriodId) {
outputStreamOffsetUs = offsetUs;
streamFormat = formats[0]; streamFormat = formats[0];
if (!isCuesWithTiming(streamFormat)) { if (!isCuesWithTiming(streamFormat)) {
assertLegacyDecodingEnabledIfRequired(); assertLegacyDecodingEnabledIfRequired();
@ -462,7 +459,6 @@ 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; lastRendererPositionUs = C.TIME_UNSET;
if (subtitleDecoder != null) { if (subtitleDecoder != null) {
releaseSubtitleDecoder(); releaseSubtitleDecoder();
@ -579,9 +575,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@SideEffectFree @SideEffectFree
private long getPresentationTimeUs(long positionUs) { private long getPresentationTimeUs(long positionUs) {
checkState(positionUs != C.TIME_UNSET); checkState(positionUs != C.TIME_UNSET);
checkState(outputStreamOffsetUs != C.TIME_UNSET); return positionUs - getStreamOffsetUs();
return positionUs - outputStreamOffsetUs;
} }
@RequiresNonNull("streamFormat") @RequiresNonNull("streamFormat")

View File

@ -153,7 +153,6 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
private int consecutiveDroppedFrameCount; private int consecutiveDroppedFrameCount;
private int buffersInCodecCount; private int buffersInCodecCount;
private long lastRenderTimeUs; private long lastRenderTimeUs;
private long outputStreamOffsetUs;
/** Decoder event counters used for debugging purposes. */ /** Decoder event counters used for debugging purposes. */
protected DecoderCounters decoderCounters; protected DecoderCounters decoderCounters;
@ -342,10 +341,8 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
long offsetUs, long offsetUs,
MediaSource.MediaPeriodId mediaPeriodId) MediaSource.MediaPeriodId mediaPeriodId)
throws ExoPlaybackException { throws ExoPlaybackException {
// TODO: This shouldn't just update the output stream offset as long as there are still buffers // TODO: This code should make sure to render the first frame of the next stream if the playback
// of the previous stream in the decoder. It should also make sure to render the first frame of // position reached the new stream.
// the next stream if the playback position reached the new stream.
outputStreamOffsetUs = offsetUs;
super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId); super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId);
} }
@ -875,7 +872,9 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
outputFormat = formatQueue.pollFirst(); outputFormat = formatQueue.pollFirst();
} }
long presentationTimeUs = bufferTimeUs - outputStreamOffsetUs; // TODO: This shouldn't just use the input stream offset and we should correctly track the
// output stream offset after decoding instead.
long presentationTimeUs = bufferTimeUs - getStreamOffsetUs();
if (shouldForceRender(earlyUs)) { if (shouldForceRender(earlyUs)) {
renderOutputBuffer(outputBuffer, presentationTimeUs, checkNotNull(outputFormat)); renderOutputBuffer(outputBuffer, presentationTimeUs, checkNotNull(outputFormat));
return true; return true;

View File

@ -28,7 +28,6 @@ import androidx.media3.exoplayer.ExoPlaybackException;
import androidx.media3.exoplayer.FormatHolder; import androidx.media3.exoplayer.FormatHolder;
import androidx.media3.exoplayer.Renderer; import androidx.media3.exoplayer.Renderer;
import androidx.media3.exoplayer.RendererCapabilities; import androidx.media3.exoplayer.RendererCapabilities;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult; import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -43,7 +42,6 @@ public final class CameraMotionRenderer extends BaseRenderer {
private final DecoderInputBuffer buffer; private final DecoderInputBuffer buffer;
private final ParsableByteArray scratch; private final ParsableByteArray scratch;
private long offsetUs;
@Nullable private CameraMotionListener listener; @Nullable private CameraMotionListener listener;
private long lastTimestampUs; private long lastTimestampUs;
@ -75,15 +73,6 @@ public final class CameraMotionRenderer extends BaseRenderer {
} }
} }
@Override
protected void onStreamChanged(
Format[] formats,
long startPositionUs,
long offsetUs,
MediaSource.MediaPeriodId mediaPeriodId) {
this.offsetUs = offsetUs;
}
@Override @Override
protected void onPositionReset(long positionUs, boolean joining) { protected void onPositionReset(long positionUs, boolean joining) {
lastTimestampUs = Long.MIN_VALUE; lastTimestampUs = Long.MIN_VALUE;
@ -118,7 +107,7 @@ public final class CameraMotionRenderer extends BaseRenderer {
continue; continue;
} }
Util.castNonNull(listener).onCameraMotion(lastTimestampUs - offsetUs, rotation); Util.castNonNull(listener).onCameraMotion(lastTimestampUs - getStreamOffsetUs(), rotation);
} }
} }

View File

@ -43,7 +43,6 @@ public class FakeVideoRenderer extends FakeRenderer {
private final AtomicReference<VideoSize> videoSizeRef = new AtomicReference<>(); private final AtomicReference<VideoSize> videoSizeRef = new AtomicReference<>();
private @MonotonicNonNull Format format; private @MonotonicNonNull Format format;
@Nullable private Object output; @Nullable private Object output;
private long streamOffsetUs;
private boolean renderedFirstFrameAfterReset; private boolean renderedFirstFrameAfterReset;
private boolean mayRenderFirstFrameAfterEnableIfNotStarted; private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
private boolean renderedFirstFrameAfterEnable; private boolean renderedFirstFrameAfterEnable;
@ -73,7 +72,6 @@ public class FakeVideoRenderer extends FakeRenderer {
MediaSource.MediaPeriodId mediaPeriodId) MediaSource.MediaPeriodId mediaPeriodId)
throws ExoPlaybackException { throws ExoPlaybackException {
super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId); super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId);
streamOffsetUs = offsetUs;
renderedFirstFrameAfterReset = false; renderedFirstFrameAfterReset = false;
} }
@ -150,7 +148,7 @@ public class FakeVideoRenderer extends FakeRenderer {
? (getState() == Renderer.STATE_STARTED ? (getState() == Renderer.STATE_STARTED
|| mayRenderFirstFrameAfterEnableIfNotStarted) || mayRenderFirstFrameAfterEnableIfNotStarted)
: !renderedFirstFrameAfterReset); : !renderedFirstFrameAfterReset);
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= streamOffsetUs; shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= getStreamOffsetUs();
@Nullable Object output = this.output; @Nullable Object output = this.output;
if (shouldProcess && !renderedFirstFrameAfterReset && output != null) { if (shouldProcess && !renderedFirstFrameAfterReset && output != null) {
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format); @MonotonicNonNull Format format = Assertions.checkNotNull(this.format);

View File

@ -42,7 +42,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/* package */ abstract class ExoAssetLoaderBaseRenderer extends BaseRenderer { /* package */ abstract class ExoAssetLoaderBaseRenderer extends BaseRenderer {
protected long streamStartPositionUs; protected long streamStartPositionUs;
protected long streamOffsetUs;
protected @MonotonicNonNull SampleConsumer sampleConsumer; protected @MonotonicNonNull SampleConsumer sampleConsumer;
protected @MonotonicNonNull Codec decoder; protected @MonotonicNonNull Codec decoder;
protected boolean isEnded; protected boolean isEnded;
@ -131,7 +130,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
long offsetUs, long offsetUs,
MediaSource.MediaPeriodId mediaPeriodId) { MediaSource.MediaPeriodId mediaPeriodId) {
this.streamStartPositionUs = startPositionUs; this.streamStartPositionUs = startPositionUs;
this.streamOffsetUs = offsetUs;
} }
@Override @Override

View File

@ -130,6 +130,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
ByteBuffer inputBytes = checkNotNull(inputBuffer.data); ByteBuffer inputBytes = checkNotNull(inputBuffer.data);
if (sefVideoSlowMotionFlattener != null) { if (sefVideoSlowMotionFlattener != null) {
long streamOffsetUs = getStreamOffsetUs();
long presentationTimeUs = inputBuffer.timeUs - streamOffsetUs; long presentationTimeUs = inputBuffer.timeUs - streamOffsetUs;
boolean shouldDropInputBuffer = boolean shouldDropInputBuffer =
sefVideoSlowMotionFlattener.dropOrTransformSample(inputBytes, presentationTimeUs); sefVideoSlowMotionFlattener.dropOrTransformSample(inputBytes, presentationTimeUs);

View File

@ -300,7 +300,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Nullable private ExoPlaybackException pendingExoPlaybackException; @Nullable private ExoPlaybackException pendingExoPlaybackException;
private boolean inputStreamPending; private boolean inputStreamPending;
private long streamStartPositionUs; private long streamStartPositionUs;
private long streamOffsetUs;
private boolean mayRenderStartOfStream; private boolean mayRenderStartOfStream;
private long offsetToCompositionTimeUs; private long offsetToCompositionTimeUs;
@ -311,7 +310,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
videoSink = checkStateNotNull(sequencePlayerRenderersWrapper.videoSink); videoSink = checkStateNotNull(sequencePlayerRenderersWrapper.videoSink);
videoEffects = ImmutableList.of(); videoEffects = ImmutableList.of();
streamStartPositionUs = C.TIME_UNSET; streamStartPositionUs = C.TIME_UNSET;
streamOffsetUs = C.TIME_UNSET;
} }
// ImageRenderer methods // ImageRenderer methods
@ -397,7 +395,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
checkState(getTimeline().getWindowCount() == 1); checkState(getTimeline().getWindowCount() == 1);
super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId); super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId);
streamStartPositionUs = startPositionUs; streamStartPositionUs = startPositionUs;
streamOffsetUs = offsetUs;
int mediaItemIndex = getTimeline().getIndexOfPeriod(mediaPeriodId.periodUid); int mediaItemIndex = getTimeline().getIndexOfPeriod(mediaPeriodId.periodUid);
editedMediaItem = editedMediaItem =
sequencePlayerRenderersWrapper.sequence.editedMediaItems.get(mediaItemIndex); sequencePlayerRenderersWrapper.sequence.editedMediaItems.get(mediaItemIndex);
@ -429,11 +426,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
long positionUs, long elapsedRealtimeUs, Bitmap outputImage, long timeUs) { long positionUs, long elapsedRealtimeUs, Bitmap outputImage, long timeUs) {
if (inputStreamPending) { if (inputStreamPending) {
checkState(streamStartPositionUs != C.TIME_UNSET); checkState(streamStartPositionUs != C.TIME_UNSET);
checkState(streamOffsetUs != C.TIME_UNSET);
videoSink.setPendingVideoEffects(videoEffects); videoSink.setPendingVideoEffects(videoEffects);
videoSink.setStreamTimestampInfo( videoSink.setStreamTimestampInfo(
streamStartPositionUs, streamStartPositionUs,
streamOffsetUs, getStreamOffsetUs(),
/* bufferTimestampAdjustmentUs= */ offsetToCompositionTimeUs, /* bufferTimestampAdjustmentUs= */ offsetToCompositionTimeUs,
getLastResetPositionUs()); getLastResetPositionUs());
videoSink.onInputStreamChanged( videoSink.onInputStreamChanged(
@ -451,6 +447,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} }
private ConstantRateTimestampIterator createTimestampIterator(long positionUs) { private ConstantRateTimestampIterator createTimestampIterator(long positionUs) {
long streamOffsetUs = getStreamOffsetUs();
long imageBaseTimestampUs = streamOffsetUs + offsetToCompositionTimeUs; long imageBaseTimestampUs = streamOffsetUs + offsetToCompositionTimeUs;
long positionWithinImage = positionUs - streamOffsetUs; long positionWithinImage = positionUs - streamOffsetUs;
long firstBitmapTimeUs = imageBaseTimestampUs + positionWithinImage; long firstBitmapTimeUs = imageBaseTimestampUs + positionWithinImage;