Always signal stream changes through render control in DefaultVideoSink

This is part of an effort to remove VideoSink.onRendererEnabled,
which is a step towards making the VideoSink interface renderer
agnostic. The plan is to merge onRendererEnabled, setStreamTimestampInfo
and onInputStreamChanged into a single method in VideoSink.

This change is a no-op refactoring

PiperOrigin-RevId: 735293983
This commit is contained in:
kimvde 2025-03-10 02:17:49 -07:00 committed by Copybara-Service
parent 91ecc16198
commit a4442a6cc5
3 changed files with 25 additions and 17 deletions

View File

@ -18,6 +18,7 @@ package androidx.media3.exoplayer.video;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY; import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY;
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_STARTED; import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_STARTED;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -87,7 +88,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public void onRendererEnabled(boolean mayRenderStartOfStream) { public void onRendererEnabled(boolean mayRenderStartOfStream) {
int firstFrameReleaseInstruction = int firstFrameReleaseInstruction =
mayRenderStartOfStream ? RELEASE_FIRST_FRAME_IMMEDIATELY : RELEASE_FIRST_FRAME_WHEN_STARTED; mayRenderStartOfStream ? RELEASE_FIRST_FRAME_IMMEDIATELY : RELEASE_FIRST_FRAME_WHEN_STARTED;
videoFrameReleaseControl.onStreamChanged(firstFrameReleaseInstruction); videoFrameRenderControl.onStreamChanged(firstFrameReleaseInstruction, streamStartPositionUs);
} }
@Override @Override
@ -179,7 +180,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override @Override
public void setStreamTimestampInfo(long streamStartPositionUs, long bufferTimestampAdjustmentUs) { public void setStreamTimestampInfo(long streamStartPositionUs, long bufferTimestampAdjustmentUs) {
if (streamStartPositionUs != this.streamStartPositionUs) { if (streamStartPositionUs != this.streamStartPositionUs) {
videoFrameRenderControl.onStreamStartPositionChanged(streamStartPositionUs); videoFrameRenderControl.onStreamChanged(
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED, streamStartPositionUs);
this.streamStartPositionUs = streamStartPositionUs; this.streamStartPositionUs = streamStartPositionUs;
} }
this.bufferTimestampAdjustmentUs = bufferTimestampAdjustmentUs; this.bufferTimestampAdjustmentUs = bufferTimestampAdjustmentUs;

View File

@ -110,14 +110,10 @@ import androidx.media3.exoplayer.ExoPlaybackException;
lastPresentationTimeUs = C.TIME_UNSET; lastPresentationTimeUs = C.TIME_UNSET;
if (streamStartPositionsUs.size() > 0) { if (streamStartPositionsUs.size() > 0) {
// There is a pending streaming start position change. If seeking within the same stream, keep // There is a pending streaming start position change. If seeking within the same stream, keep
// the pending start position with min timestamp to ensure the start position is applied on // the pending start position with max timestamp to ensure the start position is applied on
// the frames after flushing. Otherwise if seeking to another stream, a new start position // the frames after flushing. Otherwise if seeking to another stream, a new start position
// will be set before a new frame arrives so we'll be able to apply the new start position. // will be set before a new frame arrives so we'll be able to apply the new start position.
long lastStartPositionUs = getLastAndClear(streamStartPositionsUs); outputStreamStartPositionUs = getLastAndClear(streamStartPositionsUs);
// Input timestamps should always be positive because they are offset by ExoPlayer. Adding a
// position to the queue with timestamp 0 should therefore always apply it as long as it is
// the only position in the queue.
streamStartPositionsUs.add(/* timestamp= */ 0, lastStartPositionUs);
} }
if (videoSizes.size() > 0) { if (videoSizes.size() > 0) {
// Do not clear the last pending video size, we still want to report the size change after a // Do not clear the last pending video size, we still want to report the size change after a
@ -137,8 +133,8 @@ import androidx.media3.exoplayer.ExoPlaybackException;
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
while (!presentationTimestampsUs.isEmpty()) { while (!presentationTimestampsUs.isEmpty()) {
long presentationTimeUs = presentationTimestampsUs.element(); long presentationTimeUs = presentationTimestampsUs.element();
// Check whether this buffer comes with a new stream start position. // Check whether this buffer comes with a new stream.
if (maybeUpdateOutputStreamStartPosition(presentationTimeUs)) { if (maybeUpdateOutputStream(presentationTimeUs)) {
videoFrameReleaseControl.onStreamChanged( videoFrameReleaseControl.onStreamChanged(
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED); RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED);
} }
@ -182,10 +178,17 @@ import androidx.media3.exoplayer.ExoPlaybackException;
new VideoSize(width, height)); new VideoSize(width, height));
} }
public void onStreamStartPositionChanged(long streamStartPositionUs) { public void onStreamChanged(
streamStartPositionsUs.add( @VideoFrameReleaseControl.FirstFrameReleaseInstruction int firstFrameReleaseInstruction,
latestInputPresentationTimeUs == C.TIME_UNSET ? 0 : latestInputPresentationTimeUs + 1, long streamStartPositionUs) {
streamStartPositionUs); if (presentationTimestampsUs.isEmpty()) {
videoFrameReleaseControl.onStreamChanged(firstFrameReleaseInstruction);
outputStreamStartPositionUs = streamStartPositionUs;
} else {
streamStartPositionsUs.add(
latestInputPresentationTimeUs == C.TIME_UNSET ? 0 : latestInputPresentationTimeUs + 1,
streamStartPositionUs);
}
} }
/** /**
@ -242,7 +245,7 @@ import androidx.media3.exoplayer.ExoPlaybackException;
renderTimeNs, presentationTimeUs, videoFrameReleaseControl.onFrameReleasedIsFirstFrame()); renderTimeNs, presentationTimeUs, videoFrameReleaseControl.onFrameReleasedIsFirstFrame());
} }
private boolean maybeUpdateOutputStreamStartPosition(long presentationTimeUs) { private boolean maybeUpdateOutputStream(long presentationTimeUs) {
@Nullable @Nullable
Long newOutputStreamStartPositionUs = streamStartPositionsUs.pollFloor(presentationTimeUs); Long newOutputStreamStartPositionUs = streamStartPositionsUs.pollFloor(presentationTimeUs);
if (newOutputStreamStartPositionUs != null if (newOutputStreamStartPositionUs != null

View File

@ -16,6 +16,7 @@
package androidx.media3.exoplayer.video; package androidx.media3.exoplayer.video;
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY; import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_IMMEDIATELY;
import static androidx.media3.exoplayer.video.VideoFrameReleaseControl.RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -122,7 +123,8 @@ public class VideoFrameRenderControlTest {
videoFrameReleaseControl.onStarted(); videoFrameReleaseControl.onStarted();
videoFrameRenderControl.onVideoSizeChanged( videoFrameRenderControl.onVideoSizeChanged(
/* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT); /* width= */ VIDEO_WIDTH, /* height= */ VIDEO_HEIGHT);
videoFrameRenderControl.onStreamStartPositionChanged(/* streamStartPositionUs= */ 10_000); videoFrameRenderControl.onStreamChanged(
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED, /* streamStartPositionUs= */ 10_000);
videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 0); videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 0);
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
@ -136,7 +138,8 @@ public class VideoFrameRenderControlTest {
// 10 milliseconds pass // 10 milliseconds pass
clock.advanceTime(/* timeDiffMs= */ 10); clock.advanceTime(/* timeDiffMs= */ 10);
videoFrameRenderControl.onStreamStartPositionChanged(/* streamStartPositionUs= */ 20_000); videoFrameRenderControl.onStreamChanged(
RELEASE_FIRST_FRAME_WHEN_PREVIOUS_STREAM_PROCESSED, /* streamStartPositionUs= */ 20_000);
videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 10_000); videoFrameRenderControl.onFrameAvailableForRendering(/* presentationTimeUs= */ 10_000);
videoFrameRenderControl.render(/* positionUs= */ 10_000, /* elapsedRealtimeUs= */ 0); videoFrameRenderControl.render(/* positionUs= */ 10_000, /* elapsedRealtimeUs= */ 0);