mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Make sure the first frame is force rendered.
PiperOrigin-RevId: 505960752
This commit is contained in:
parent
e7ac678ba4
commit
baf8ee570c
1 changed files with 64 additions and 36 deletions
|
|
@ -685,8 +685,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
Size outputResolution = (Size) checkNotNull(message);
|
Size outputResolution = (Size) checkNotNull(message);
|
||||||
if (outputResolution.getWidth() != 0
|
if (outputResolution.getWidth() != 0
|
||||||
&& outputResolution.getHeight() != 0
|
&& outputResolution.getHeight() != 0
|
||||||
&& displaySurface != null
|
&& displaySurface != null) {
|
||||||
&& frameProcessorManager.isEnabled()) {
|
|
||||||
frameProcessorManager.setOutputSurfaceInfo(displaySurface, outputResolution);
|
frameProcessorManager.setOutputSurfaceInfo(displaySurface, outputResolution);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1166,19 +1165,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
long elapsedSinceLastRenderUs = elapsedRealtimeNowUs - lastRenderRealtimeUs;
|
boolean forceRenderOutputBuffer = shouldForceRender(positionUs, earlyUs);
|
||||||
boolean shouldRenderFirstFrame =
|
|
||||||
!renderedFirstFrameAfterEnable
|
|
||||||
? (isStarted || mayRenderFirstFrameAfterEnableIfNotStarted)
|
|
||||||
: !renderedFirstFrameAfterReset;
|
|
||||||
// Don't force output until we joined and the position reached the current stream.
|
|
||||||
boolean forceRenderOutputBuffer =
|
|
||||||
joiningDeadlineMs == C.TIME_UNSET
|
|
||||||
&& positionUs >= outputStreamOffsetUs
|
|
||||||
&& (shouldRenderFirstFrame
|
|
||||||
|| (isStarted && shouldForceRenderOutputBuffer(earlyUs, elapsedSinceLastRenderUs)));
|
|
||||||
if (forceRenderOutputBuffer) {
|
if (forceRenderOutputBuffer) {
|
||||||
// TODO(b/238302341): Handle releasing the force rendered frames in FrameProcessor.
|
|
||||||
boolean notifyFrameMetaDataListener;
|
boolean notifyFrameMetaDataListener;
|
||||||
if (frameProcessorManager.isEnabled()) {
|
if (frameProcessorManager.isEnabled()) {
|
||||||
notifyFrameMetaDataListener = false;
|
notifyFrameMetaDataListener = false;
|
||||||
|
|
@ -1271,6 +1259,21 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether a buffer or a processed frame should be force rendered. */
|
||||||
|
private boolean shouldForceRender(long positionUs, long earlyUs) {
|
||||||
|
boolean isStarted = getState() == STATE_STARTED;
|
||||||
|
boolean shouldRenderFirstFrame =
|
||||||
|
!renderedFirstFrameAfterEnable
|
||||||
|
? (isStarted || mayRenderFirstFrameAfterEnableIfNotStarted)
|
||||||
|
: !renderedFirstFrameAfterReset;
|
||||||
|
long elapsedSinceLastRenderUs = SystemClock.elapsedRealtime() * 1000 - lastRenderRealtimeUs;
|
||||||
|
// Don't force output until we joined and the position reached the current stream.
|
||||||
|
return joiningDeadlineMs == C.TIME_UNSET
|
||||||
|
&& positionUs >= getOutputStreamOffsetUs()
|
||||||
|
&& (shouldRenderFirstFrame
|
||||||
|
|| (isStarted && shouldForceRenderOutputBuffer(earlyUs, elapsedSinceLastRenderUs)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the time interval between the current player position and the buffer presentation
|
* Calculates the time interval between the current player position and the buffer presentation
|
||||||
* time.
|
* time.
|
||||||
|
|
@ -1527,16 +1530,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
* @param presentationTimeUs The presentation time of the output buffer, in microseconds.
|
* @param presentationTimeUs The presentation time of the output buffer, in microseconds.
|
||||||
*/
|
*/
|
||||||
protected void renderOutputBuffer(MediaCodecAdapter codec, int index, long presentationTimeUs) {
|
protected void renderOutputBuffer(MediaCodecAdapter codec, int index, long presentationTimeUs) {
|
||||||
if (!frameProcessorManager.isEnabled()) {
|
|
||||||
maybeNotifyVideoSizeChanged(decodedVideoSize);
|
|
||||||
}
|
|
||||||
TraceUtil.beginSection("releaseOutputBuffer");
|
TraceUtil.beginSection("releaseOutputBuffer");
|
||||||
codec.releaseOutputBuffer(index, true);
|
codec.releaseOutputBuffer(index, true);
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
|
||||||
decoderCounters.renderedOutputBufferCount++;
|
decoderCounters.renderedOutputBufferCount++;
|
||||||
consecutiveDroppedFrameCount = 0;
|
consecutiveDroppedFrameCount = 0;
|
||||||
maybeNotifyRenderedFirstFrame();
|
if (!frameProcessorManager.isEnabled()) {
|
||||||
|
lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||||
|
maybeNotifyVideoSizeChanged(decodedVideoSize);
|
||||||
|
maybeNotifyRenderedFirstFrame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1555,16 +1558,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
@RequiresApi(21)
|
@RequiresApi(21)
|
||||||
protected void renderOutputBufferV21(
|
protected void renderOutputBufferV21(
|
||||||
MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) {
|
MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) {
|
||||||
if (!frameProcessorManager.isEnabled()) {
|
|
||||||
maybeNotifyVideoSizeChanged(decodedVideoSize);
|
|
||||||
}
|
|
||||||
TraceUtil.beginSection("releaseOutputBuffer");
|
TraceUtil.beginSection("releaseOutputBuffer");
|
||||||
codec.releaseOutputBuffer(index, releaseTimeNs);
|
codec.releaseOutputBuffer(index, releaseTimeNs);
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
|
||||||
decoderCounters.renderedOutputBufferCount++;
|
decoderCounters.renderedOutputBufferCount++;
|
||||||
consecutiveDroppedFrameCount = 0;
|
consecutiveDroppedFrameCount = 0;
|
||||||
maybeNotifyRenderedFirstFrame();
|
if (!frameProcessorManager.isEnabled()) {
|
||||||
|
lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||||
|
maybeNotifyVideoSizeChanged(decodedVideoSize);
|
||||||
|
maybeNotifyRenderedFirstFrame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldUsePlaceholderSurface(MediaCodecInfo codecInfo) {
|
private boolean shouldUsePlaceholderSurface(MediaCodecInfo codecInfo) {
|
||||||
|
|
@ -2013,6 +2016,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
throw renderer.createRendererException(
|
throw renderer.createRendererException(
|
||||||
e, inputFormat, PlaybackException.ERROR_CODE_UNSPECIFIED);
|
e, inputFormat, PlaybackException.ERROR_CODE_UNSPECIFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentSurfaceAndSize != null) {
|
||||||
|
Size outputSurfaceSize = currentSurfaceAndSize.second;
|
||||||
|
frameProcessor.setOutputSurfaceInfo(
|
||||||
|
new SurfaceInfo(
|
||||||
|
currentSurfaceAndSize.first,
|
||||||
|
outputSurfaceSize.getWidth(),
|
||||||
|
outputSurfaceSize.getHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
setInputFormat(inputFormat);
|
setInputFormat(inputFormat);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -2031,9 +2044,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
/**
|
/**
|
||||||
* Sets the output surface info.
|
* Sets the output surface info.
|
||||||
*
|
*
|
||||||
* <p>Caller must ensure the {@code FrameProcessorManager} {@link #isEnabled()} before calling
|
|
||||||
* this method.
|
|
||||||
*
|
|
||||||
* @param outputSurface The {@link Surface} to which {@link FrameProcessor} outputs.
|
* @param outputSurface The {@link Surface} to which {@link FrameProcessor} outputs.
|
||||||
* @param outputResolution The {@link Size} of the output resolution.
|
* @param outputResolution The {@link Size} of the output resolution.
|
||||||
*/
|
*/
|
||||||
|
|
@ -2043,10 +2053,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
&& currentSurfaceAndSize.second.equals(outputResolution)) {
|
&& currentSurfaceAndSize.second.equals(outputResolution)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checkNotNull(frameProcessor)
|
currentSurfaceAndSize = Pair.create(outputSurface, outputResolution);
|
||||||
.setOutputSurfaceInfo(
|
if (isEnabled()) {
|
||||||
new SurfaceInfo(
|
checkNotNull(frameProcessor)
|
||||||
outputSurface, outputResolution.getWidth(), outputResolution.getHeight()));
|
.setOutputSurfaceInfo(
|
||||||
|
new SurfaceInfo(
|
||||||
|
outputSurface, outputResolution.getWidth(), outputResolution.getHeight()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2148,6 +2161,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
// Locking the entire releasing flow may block the FrameProcessor thread running
|
// Locking the entire releasing flow may block the FrameProcessor thread running
|
||||||
// onOutputFrameAvailable().
|
// onOutputFrameAvailable().
|
||||||
while (!processedFramesTimestampsUs.isEmpty()) {
|
while (!processedFramesTimestampsUs.isEmpty()) {
|
||||||
|
boolean isStarted = renderer.getState() == STATE_STARTED;
|
||||||
long bufferPresentationTimeUs = checkNotNull(processedFramesTimestampsUs.peek());
|
long bufferPresentationTimeUs = checkNotNull(processedFramesTimestampsUs.peek());
|
||||||
long earlyUs =
|
long earlyUs =
|
||||||
renderer.calculateEarlyTimeUs(
|
renderer.calculateEarlyTimeUs(
|
||||||
|
|
@ -2155,7 +2169,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
elapsedRealtimeUs,
|
elapsedRealtimeUs,
|
||||||
SystemClock.elapsedRealtime() * 1000,
|
SystemClock.elapsedRealtime() * 1000,
|
||||||
bufferPresentationTimeUs,
|
bufferPresentationTimeUs,
|
||||||
renderer.getState() == STATE_STARTED);
|
isStarted);
|
||||||
|
|
||||||
|
boolean shouldReleaseFrameImmediately = renderer.shouldForceRender(positionUs, earlyUs);
|
||||||
|
if (shouldReleaseFrameImmediately) {
|
||||||
|
releaseOutputFrame(FrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Only release frames that are reasonably close to presentation.
|
// Only release frames that are reasonably close to presentation.
|
||||||
// This way frameReleaseHelper.onNextFrame() is called only once for each frame.
|
// This way frameReleaseHelper.onNextFrame() is called only once for each frame.
|
||||||
|
|
@ -2173,8 +2193,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
// FrameProcessor input frames in this case.
|
// FrameProcessor input frames in this case.
|
||||||
boolean isLastFrame = processedLastFrame && processedFramesTimestampsUs.size() == 1;
|
boolean isLastFrame = processedLastFrame && processedFramesTimestampsUs.size() == 1;
|
||||||
if (renderer.shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs, isLastFrame)) {
|
if (renderer.shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs, isLastFrame)) {
|
||||||
frameProcessor.releaseOutputFrame(FrameProcessor.DROP_OUTPUT_FRAME);
|
releaseOutputFrame(FrameProcessor.DROP_OUTPUT_FRAME);
|
||||||
processedFramesTimestampsUs.remove();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2190,8 +2209,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
pendingOutputSizeChangeNotificationTimeUs = C.TIME_UNSET;
|
pendingOutputSizeChangeNotificationTimeUs = C.TIME_UNSET;
|
||||||
renderer.maybeNotifyVideoSizeChanged(processedFrameSize);
|
renderer.maybeNotifyVideoSizeChanged(processedFrameSize);
|
||||||
}
|
}
|
||||||
frameProcessor.releaseOutputFrame(adjustedFrameReleaseTimeNs);
|
releaseOutputFrame(adjustedFrameReleaseTimeNs);
|
||||||
processedFramesTimestampsUs.remove();
|
|
||||||
if (isLastFrame) {
|
if (isLastFrame) {
|
||||||
releasedLastFrame = true;
|
releasedLastFrame = true;
|
||||||
}
|
}
|
||||||
|
|
@ -2217,6 +2235,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
canEnableFrameProcessing = true;
|
canEnableFrameProcessing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void releaseOutputFrame(long releaseTimeNs) {
|
||||||
|
checkStateNotNull(frameProcessor);
|
||||||
|
frameProcessor.releaseOutputFrame(releaseTimeNs);
|
||||||
|
processedFramesTimestampsUs.remove();
|
||||||
|
renderer.lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||||
|
if (releaseTimeNs != FrameProcessor.DROP_OUTPUT_FRAME) {
|
||||||
|
renderer.maybeNotifyRenderedFirstFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class FrameProcessorAccessor {
|
private static final class FrameProcessorAccessor {
|
||||||
|
|
||||||
private static @MonotonicNonNull Constructor<?> scaleToFitTransformationBuilderConstructor;
|
private static @MonotonicNonNull Constructor<?> scaleToFitTransformationBuilderConstructor;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue