Call VideoFrameReleaseControl.isReady from VideoSink when enabled

PiperOrigin-RevId: 646385384
This commit is contained in:
kimvde 2024-06-25 01:47:49 -07:00 committed by Copybara-Service
parent babc9c69c6
commit 304c4e41f8
8 changed files with 51 additions and 38 deletions

View File

@ -426,8 +426,9 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
}
}
private boolean isReady() {
return pendingFlushCount == 0 && videoFrameRenderControl.isReady();
private boolean isReady(boolean rendererOtherwiseReady) {
return videoFrameRenderControl.isReady(
/* rendererOtherwiseReady= */ rendererOtherwiseReady && pendingFlushCount == 0);
}
private boolean hasReleasedFrame(long presentationTimeUs) {
@ -585,8 +586,9 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
}
@Override
public boolean isReady() {
return isInitialized() && CompositingVideoSinkProvider.this.isReady();
public boolean isReady(boolean rendererOtherwiseReady) {
return CompositingVideoSinkProvider.this.isReady(
/* rendererOtherwiseReady= */ rendererOtherwiseReady && isInitialized());
}
@Override

View File

@ -748,15 +748,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Override
public boolean isReady() {
boolean readyToReleaseFrames = super.isReady() && (videoSink == null || videoSink.isReady());
if (readyToReleaseFrames
boolean rendererOtherwiseReady = super.isReady();
if (videoSink != null) {
return videoSink.isReady(rendererOtherwiseReady);
}
if (rendererOtherwiseReady
&& ((placeholderSurface != null && displaySurface == placeholderSurface)
|| getCodec() == null
|| tunneling)) {
// Not releasing frames.
return true;
}
return videoFrameReleaseControl.isReady(readyToReleaseFrames);
return videoFrameReleaseControl.isReady(rendererOtherwiseReady);
}
@Override

View File

@ -277,12 +277,14 @@ public final class VideoFrameReleaseControl {
/**
* Whether the release control is ready to start playback.
*
* @see Renderer#isReady()
* @param rendererReady Whether the renderer is ready.
* <p>The renderer should be {@linkplain Renderer#isReady() ready} if and only if the release
* control is ready.
*
* @param rendererOtherwiseReady Whether the renderer is ready except for the release control.
* @return Whether the release control is ready.
*/
public boolean isReady(boolean rendererReady) {
if (rendererReady && firstFrameState == C.FIRST_FRAME_RENDERED) {
public boolean isReady(boolean rendererOtherwiseReady) {
if (rendererOtherwiseReady && firstFrameState == C.FIRST_FRAME_RENDERED) {
// Ready. If we were joining then we've now joined, so clear the joining deadline.
joiningDeadlineMs = C.TIME_UNSET;
return true;

View File

@ -123,8 +123,8 @@ import androidx.media3.exoplayer.ExoPlaybackException;
}
/** Returns whether the renderer is ready. */
public boolean isReady() {
return videoFrameReleaseControl.isReady(/* rendererReady= */ true);
public boolean isReady(boolean rendererOtherwiseReady) {
return videoFrameReleaseControl.isReady(rendererOtherwiseReady);
}
/**

View File

@ -28,6 +28,7 @@ import androidx.media3.common.VideoSize;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.TimestampIterator;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.Renderer;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -148,8 +149,13 @@ public interface VideoSink {
/**
* Returns whether the video sink is able to immediately render media from the current position.
*
* <p>The renderer should be {@linkplain Renderer#isReady() ready} if and only if the video sink
* is ready.
*
* @param rendererOtherwiseReady Whether the renderer is ready except for the video sink.
*/
boolean isReady();
boolean isReady(boolean rendererOtherwiseReady);
/**
* Returns whether all queued video frames have been rendered, including the frame marked as last

View File

@ -31,8 +31,8 @@ public class VideoFrameReleaseControlTest {
public void isReady_onNewInstance_returnsFalse() {
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ true)).isFalse();
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ false)).isFalse();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ true)).isFalse();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ false)).isFalse();
}
@Test
@ -40,7 +40,7 @@ public class VideoFrameReleaseControlTest {
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
assertThat(videoFrameReleaseControl.onFrameReleasedIsFirstFrame()).isTrue();
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ true)).isTrue();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ true)).isTrue();
}
@Test
@ -52,7 +52,7 @@ public class VideoFrameReleaseControlTest {
videoFrameReleaseControl.join(/* renderNextFrameImmediately= */ true);
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ false)).isTrue();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ false)).isTrue();
}
@Test
@ -64,7 +64,7 @@ public class VideoFrameReleaseControlTest {
videoFrameReleaseControl.join(/* renderNextFrameImmediately= */ false);
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ false)).isTrue();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ false)).isTrue();
}
@Test
@ -75,11 +75,11 @@ public class VideoFrameReleaseControlTest {
videoFrameReleaseControl.setClock(clock);
videoFrameReleaseControl.join(/* renderNextFrameImmediately= */ true);
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ false)).isTrue();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ false)).isTrue();
clock.advanceTime(/* timeDiffMs= */ 101);
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ false)).isFalse();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ false)).isFalse();
}
@Test
@ -90,11 +90,11 @@ public class VideoFrameReleaseControlTest {
videoFrameReleaseControl.setClock(clock);
videoFrameReleaseControl.join(/* renderNextFrameImmediately= */ false);
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ false)).isTrue();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ false)).isTrue();
clock.advanceTime(/* timeDiffMs= */ 101);
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ false)).isFalse();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ false)).isFalse();
}
@Test
@ -132,10 +132,10 @@ public class VideoFrameReleaseControlTest {
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
videoFrameReleaseControl.onFrameReleasedIsFirstFrame();
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ true)).isTrue();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ true)).isTrue();
videoFrameReleaseControl.reset();
assertThat(videoFrameReleaseControl.isReady(/* rendererReady= */ true)).isFalse();
assertThat(videoFrameReleaseControl.isReady(/* rendererOtherwiseReady= */ true)).isFalse();
}
@Test

View File

@ -43,7 +43,7 @@ public class VideoFrameRenderControlTest {
new VideoFrameRenderControl(
mock(VideoFrameRenderControl.FrameRenderer.class), createVideoFrameReleaseControl());
assertThat(videoFrameRenderControl.isReady()).isFalse();
assertThat(videoFrameRenderControl.isReady(/* rendererOtherwiseReady= */ true)).isFalse();
}
@Test
@ -60,7 +60,7 @@ public class VideoFrameRenderControlTest {
videoFrameRenderControl.onOutputFrameAvailableForRendering(/* presentationTimeUs= */ 0);
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
assertThat(videoFrameRenderControl.isReady()).isTrue();
assertThat(videoFrameRenderControl.isReady(/* rendererOtherwiseReady= */ true)).isTrue();
InOrder inOrder = Mockito.inOrder(frameRenderer);
inOrder
.verify(frameRenderer)
@ -92,7 +92,7 @@ public class VideoFrameRenderControlTest {
videoFrameRenderControl.onOutputFrameAvailableForRendering(/* presentationTimeUs= */ 10_000);
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
assertThat(videoFrameRenderControl.isReady()).isTrue();
assertThat(videoFrameRenderControl.isReady(/* rendererOtherwiseReady= */ true)).isTrue();
InOrder inOrder = Mockito.inOrder(frameRenderer);
inOrder
.verify(frameRenderer)
@ -141,7 +141,7 @@ public class VideoFrameRenderControlTest {
videoFrameRenderControl.onOutputFrameAvailableForRendering(/* presentationTimeUs= */ 0);
videoFrameRenderControl.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
assertThat(videoFrameRenderControl.isReady()).isTrue();
assertThat(videoFrameRenderControl.isReady(/* rendererOtherwiseReady= */ true)).isTrue();
InOrder inOrder = Mockito.inOrder(frameRenderer);
inOrder
.verify(frameRenderer)

View File

@ -48,7 +48,6 @@ import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.text.TextOutput;
import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
import androidx.media3.exoplayer.video.MediaCodecVideoRenderer;
import androidx.media3.exoplayer.video.VideoFrameReleaseControl;
import androidx.media3.exoplayer.video.VideoRendererEventListener;
import androidx.media3.exoplayer.video.VideoSink;
import com.google.common.collect.ImmutableList;
@ -296,7 +295,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final SequencePlayerRenderersWrapper sequencePlayerRenderersWrapper;
private final CompositingVideoSinkProvider compositingVideoSinkProvider;
private final VideoSink videoSink;
private final VideoFrameReleaseControl videoFrameReleaseControl;
private ImmutableList<Effect> videoEffects;
private @MonotonicNonNull ConstantRateTimestampIterator timestampIterator;
@ -314,8 +312,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
compositingVideoSinkProvider =
checkStateNotNull(sequencePlayerRenderersWrapper.compositingVideoSinkProvider);
videoSink = compositingVideoSinkProvider.getSink();
videoFrameReleaseControl =
checkStateNotNull(compositingVideoSinkProvider.getVideoFrameReleaseControl());
videoEffects = ImmutableList.of();
streamOffsetUs = C.TIME_UNSET;
}
@ -357,11 +353,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public boolean isReady() {
// If the renderer was enabled with mayRenderStartOfStream set to false, meaning the image
// renderer is playing after a video, we don't need to wait until the first frame is rendered.
// If the renderer was enabled with mayRenderStartOfStream, we must wait until the first frame
// is rendered, which is checked by VideoSink.isReady().
return super.isReady() && (!mayRenderStartOfStream || videoSink.isReady());
if (mayRenderStartOfStream) {
// The image renderer is not playing after a video. We must wait until the first frame is
// rendered.
return videoSink.isReady(/* rendererOtherwiseReady= */ super.isReady());
} else {
// The image renderer is playing after a video. We don't need to wait until the first frame
// is rendered.
return super.isReady();
}
}
@Override