Decouple displaySurface from placeholderSurface

We currently use displaySurface == placeholderSurface IF codec != null
as a signal that the codec is configured with a placeholder. In the
future, this placeholder might not be needed and we can decouple this
state a bit better by leaving displaySurface == null in this case and
only using placeholderSurface instead of null when setting a Surface
to the codec.

PiperOrigin-RevId: 652391729
This commit is contained in:
tonihei 2024-07-15 01:02:14 -07:00 committed by Copybara-Service
parent d035b745cd
commit e96ca5a242
2 changed files with 45 additions and 56 deletions

View File

@ -671,14 +671,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
new VideoSink.Listener() { new VideoSink.Listener() {
@Override @Override
public void onFirstFrameRendered(VideoSink videoSink) { public void onFirstFrameRendered(VideoSink videoSink) {
checkStateNotNull(displaySurface); if (displaySurface != null) {
notifyRenderedFirstFrame(); notifyRenderedFirstFrame();
}
} }
@Override @Override
public void onFrameDropped(VideoSink videoSink) { public void onFrameDropped(VideoSink videoSink) {
updateDroppedBufferCounters( if (displaySurface != null) {
/* droppedInputBufferCount= */ 0, /* droppedDecoderBufferCount= */ 1); updateDroppedBufferCounters(
/* droppedInputBufferCount= */ 0, /* droppedDecoderBufferCount= */ 1);
}
} }
@Override @Override
@ -797,10 +800,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
if (videoSink != null) { if (videoSink != null) {
return videoSink.isReady(rendererOtherwiseReady); return videoSink.isReady(rendererOtherwiseReady);
} }
if (rendererOtherwiseReady if (rendererOtherwiseReady && (getCodec() == null || displaySurface == null || tunneling)) {
&& ((placeholderSurface != null && displaySurface == placeholderSurface)
|| getCodec() == null
|| tunneling)) {
// Not releasing frames. // Not releasing frames.
return true; return true;
} }
@ -862,9 +862,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
} finally { } finally {
hasSetVideoSink = false; hasSetVideoSink = false;
startPositionUs = C.TIME_UNSET; startPositionUs = C.TIME_UNSET;
if (placeholderSurface != null) { releasePlaceholderSurface();
releasePlaceholderSurface();
}
} }
} }
@ -946,20 +944,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
// Handle unsupported (i.e., non-Surface) outputs by clearing the display surface. // Handle unsupported (i.e., non-Surface) outputs by clearing the display surface.
@Nullable Surface displaySurface = output instanceof Surface ? (Surface) output : null; @Nullable Surface displaySurface = output instanceof Surface ? (Surface) output : null;
if (displaySurface == null) {
// Use a placeholder surface if possible.
if (placeholderSurface != null) {
displaySurface = placeholderSurface;
} else {
MediaCodecInfo codecInfo = getCodecInfo();
if (codecInfo != null && shouldUsePlaceholderSurface(codecInfo)) {
placeholderSurface = PlaceholderSurface.newInstance(context, codecInfo.secure);
displaySurface = placeholderSurface;
}
}
}
// We only need to update the codec if the display surface has changed.
if (this.displaySurface != displaySurface) { if (this.displaySurface != displaySurface) {
this.displaySurface = displaySurface; this.displaySurface = displaySurface;
if (videoSink == null) { if (videoSink == null) {
@ -970,14 +954,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@State int state = getState(); @State int state = getState();
@Nullable MediaCodecAdapter codec = getCodec(); @Nullable MediaCodecAdapter codec = getCodec();
if (codec != null && videoSink == null) { if (codec != null && videoSink == null) {
if (Util.SDK_INT >= 23 && displaySurface != null && !codecNeedsSetOutputSurfaceWorkaround) { MediaCodecInfo codecInfo = checkNotNull(getCodecInfo());
setOutputSurfaceV23(codec, displaySurface); boolean canUpdateSurface = hasSurfaceForCodec(codecInfo);
if (Util.SDK_INT >= 23 && canUpdateSurface && !codecNeedsSetOutputSurfaceWorkaround) {
setOutputSurfaceV23(codec, getSurfaceForCodec(codecInfo));
} else { } else {
releaseCodec(); releaseCodec();
maybeInitCodecOrBypass(); maybeInitCodecOrBypass();
} }
} }
if (displaySurface != null && displaySurface != placeholderSurface) { if (displaySurface != null) {
// If we know the video size, report it again immediately. // If we know the video size, report it again immediately.
maybeRenotifyVideoSizeChanged(); maybeRenotifyVideoSizeChanged();
if (state == STATE_STARTED) { if (state == STATE_STARTED) {
@ -999,7 +985,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
} }
} }
maybeSetupTunnelingForFirstFrame(); maybeSetupTunnelingForFirstFrame();
} else if (displaySurface != null && displaySurface != placeholderSurface) { } else if (displaySurface != null) {
// The display surface is set and unchanged. If we know the video size and/or have already // The display surface is set and unchanged. If we know the video size and/or have already
// rendered to the display surface, report these again immediately. // rendered to the display surface, report these again immediately.
maybeRenotifyVideoSizeChanged(); maybeRenotifyVideoSizeChanged();
@ -1009,7 +995,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Override @Override
protected boolean shouldInitCodec(MediaCodecInfo codecInfo) { protected boolean shouldInitCodec(MediaCodecInfo codecInfo) {
return displaySurface != null || shouldUsePlaceholderSurface(codecInfo); return hasSurfaceForCodec(codecInfo);
} }
@Override @Override
@ -1024,10 +1010,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
Format format, Format format,
@Nullable MediaCrypto crypto, @Nullable MediaCrypto crypto,
float codecOperatingRate) { float codecOperatingRate) {
if (placeholderSurface != null && placeholderSurface.secure != codecInfo.secure) {
// We can't re-use the current DummySurface instance with the new decoder.
releasePlaceholderSurface();
}
String codecMimeType = codecInfo.codecMimeType; String codecMimeType = codecInfo.codecMimeType;
codecMaxValues = getCodecMaxValues(codecInfo, format, getStreamFormats()); codecMaxValues = getCodecMaxValues(codecInfo, format, getStreamFormats());
MediaFormat mediaFormat = MediaFormat mediaFormat =
@ -1038,22 +1020,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
codecOperatingRate, codecOperatingRate,
deviceNeedsNoPostProcessWorkaround, deviceNeedsNoPostProcessWorkaround,
tunneling ? tunnelingAudioSessionId : C.AUDIO_SESSION_ID_UNSET); tunneling ? tunnelingAudioSessionId : C.AUDIO_SESSION_ID_UNSET);
if (displaySurface == null) { Surface codecSurface = getSurfaceForCodec(codecInfo);
if (!shouldUsePlaceholderSurface(codecInfo)) {
throw new IllegalStateException();
}
if (placeholderSurface == null) {
placeholderSurface = PlaceholderSurface.newInstance(context, codecInfo.secure);
}
displaySurface = placeholderSurface;
}
maybeSetKeyAllowFrameDrop(mediaFormat); maybeSetKeyAllowFrameDrop(mediaFormat);
return MediaCodecAdapter.Configuration.createForVideoDecoding( return MediaCodecAdapter.Configuration.createForVideoDecoding(
codecInfo, codecInfo, mediaFormat, format, codecSurface, crypto);
mediaFormat,
format,
videoSink != null ? videoSink.getInputSurface() : displaySurface,
crypto);
} }
@SuppressWarnings("InlinedApi") // VideoSink will check the API level @SuppressWarnings("InlinedApi") // VideoSink will check the API level
@ -1505,7 +1475,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
} }
// We are not rendering on a surface, the renderer will wait until a surface is set. // We are not rendering on a surface, the renderer will wait until a surface is set.
if (displaySurface == placeholderSurface) { if (displaySurface == null) {
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes. // Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
if (videoFrameReleaseInfo.getEarlyUs() < 30_000) { if (videoFrameReleaseInfo.getEarlyUs() < 30_000) {
skipOutputBuffer(codec, bufferIndex, presentationTimeUs); skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
@ -1843,6 +1813,32 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
} }
} }
private boolean hasSurfaceForCodec(MediaCodecInfo codecInfo) {
return displaySurface != null || shouldUsePlaceholderSurface(codecInfo);
}
/**
* Returns surface to be set on the codec. Must only be called if {@link
* #hasSurfaceForCodec(MediaCodecInfo)} returns true.
*/
private Surface getSurfaceForCodec(MediaCodecInfo codecInfo) {
if (videoSink != null) {
return videoSink.getInputSurface();
} else if (displaySurface != null) {
return displaySurface;
} else {
checkState(shouldUsePlaceholderSurface(codecInfo));
if (placeholderSurface != null && placeholderSurface.secure != codecInfo.secure) {
// We can't re-use the current placeholder surface instance with the new decoder.
releasePlaceholderSurface();
}
if (placeholderSurface == null) {
placeholderSurface = PlaceholderSurface.newInstance(context, codecInfo.secure);
}
return placeholderSurface;
}
}
private boolean shouldUsePlaceholderSurface(MediaCodecInfo codecInfo) { private boolean shouldUsePlaceholderSurface(MediaCodecInfo codecInfo) {
return Util.SDK_INT >= 23 return Util.SDK_INT >= 23
&& !tunneling && !tunneling
@ -1851,9 +1847,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
} }
private void releasePlaceholderSurface() { private void releasePlaceholderSurface() {
if (displaySurface == placeholderSurface) {
displaySurface = null;
}
if (placeholderSurface != null) { if (placeholderSurface != null) {
placeholderSurface.release(); placeholderSurface.release();
placeholderSurface = null; placeholderSurface = null;

View File

@ -173,10 +173,6 @@ public final class VideoFrameReleaseHelper {
* @param surface The new {@link Surface}, or {@code null} if the renderer does not have one. * @param surface The new {@link Surface}, or {@code null} if the renderer does not have one.
*/ */
public void onSurfaceChanged(@Nullable Surface surface) { public void onSurfaceChanged(@Nullable Surface surface) {
if (surface instanceof PlaceholderSurface) {
// We don't care about dummy surfaces for release timing, since they're not visible.
surface = null;
}
if (this.surface == surface) { if (this.surface == surface) {
return; return;
} }