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;
}
/**
* 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}. */
protected final FormatHolder getFormatHolder() {
formatHolder.clear();

View File

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

View File

@ -153,7 +153,6 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
private int consecutiveDroppedFrameCount;
private int buffersInCodecCount;
private long lastRenderTimeUs;
private long outputStreamOffsetUs;
/** Decoder event counters used for debugging purposes. */
protected DecoderCounters decoderCounters;
@ -342,10 +341,8 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
long offsetUs,
MediaSource.MediaPeriodId mediaPeriodId)
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;
// TODO: This code should make sure to render the first frame of the next stream if the playback
// position reached the new stream.
super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId);
}
@ -875,7 +872,9 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
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)) {
renderOutputBuffer(outputBuffer, presentationTimeUs, checkNotNull(outputFormat));
return true;

View File

@ -28,7 +28,6 @@ import androidx.media3.exoplayer.ExoPlaybackException;
import androidx.media3.exoplayer.FormatHolder;
import androidx.media3.exoplayer.Renderer;
import androidx.media3.exoplayer.RendererCapabilities;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
import java.nio.ByteBuffer;
@ -43,7 +42,6 @@ public final class CameraMotionRenderer extends BaseRenderer {
private final DecoderInputBuffer buffer;
private final ParsableByteArray scratch;
private long offsetUs;
@Nullable private CameraMotionListener listener;
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
protected void onPositionReset(long positionUs, boolean joining) {
lastTimestampUs = Long.MIN_VALUE;
@ -118,7 +107,7 @@ public final class CameraMotionRenderer extends BaseRenderer {
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 @MonotonicNonNull Format format;
@Nullable private Object output;
private long streamOffsetUs;
private boolean renderedFirstFrameAfterReset;
private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
private boolean renderedFirstFrameAfterEnable;
@ -73,7 +72,6 @@ public class FakeVideoRenderer extends FakeRenderer {
MediaSource.MediaPeriodId mediaPeriodId)
throws ExoPlaybackException {
super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId);
streamOffsetUs = offsetUs;
renderedFirstFrameAfterReset = false;
}
@ -150,7 +148,7 @@ public class FakeVideoRenderer extends FakeRenderer {
? (getState() == Renderer.STATE_STARTED
|| mayRenderFirstFrameAfterEnableIfNotStarted)
: !renderedFirstFrameAfterReset);
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= streamOffsetUs;
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= getStreamOffsetUs();
@Nullable Object output = this.output;
if (shouldProcess && !renderedFirstFrameAfterReset && output != null) {
@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 {
protected long streamStartPositionUs;
protected long streamOffsetUs;
protected @MonotonicNonNull SampleConsumer sampleConsumer;
protected @MonotonicNonNull Codec decoder;
protected boolean isEnded;
@ -131,7 +130,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
long offsetUs,
MediaSource.MediaPeriodId mediaPeriodId) {
this.streamStartPositionUs = startPositionUs;
this.streamOffsetUs = offsetUs;
}
@Override

View File

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

View File

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