mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Drop to keyframe in LibvpxVideoRenderer
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171517156
This commit is contained in:
parent
e887165955
commit
54d3df4b63
2 changed files with 74 additions and 17 deletions
|
|
@ -109,12 +109,12 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
private DrmSession<ExoMediaCrypto> drmSession;
|
private DrmSession<ExoMediaCrypto> drmSession;
|
||||||
private DrmSession<ExoMediaCrypto> pendingDrmSession;
|
private DrmSession<ExoMediaCrypto> pendingDrmSession;
|
||||||
|
|
||||||
@ReinitializationState
|
private @ReinitializationState int decoderReinitializationState;
|
||||||
private int decoderReinitializationState;
|
|
||||||
private boolean decoderReceivedBuffers;
|
private boolean decoderReceivedBuffers;
|
||||||
|
|
||||||
private Bitmap bitmap;
|
private Bitmap bitmap;
|
||||||
private boolean renderedFirstFrame;
|
private boolean renderedFirstFrame;
|
||||||
|
private boolean forceRenderFrame;
|
||||||
private long joiningDeadlineMs;
|
private long joiningDeadlineMs;
|
||||||
private Surface surface;
|
private Surface surface;
|
||||||
private VpxOutputBufferRenderer outputBufferRenderer;
|
private VpxOutputBufferRenderer outputBufferRenderer;
|
||||||
|
|
@ -129,6 +129,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
private long droppedFrameAccumulationStartTimeMs;
|
private long droppedFrameAccumulationStartTimeMs;
|
||||||
private int droppedFrames;
|
private int droppedFrames;
|
||||||
private int consecutiveDroppedFrameCount;
|
private int consecutiveDroppedFrameCount;
|
||||||
|
private int buffersInCodecCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param scaleToFit Whether video frames should be scaled to fit when rendering.
|
* @param scaleToFit Whether video frames should be scaled to fit when rendering.
|
||||||
|
|
@ -257,6 +258,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
|
decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
|
||||||
|
buffersInCodecCount -= outputBuffer.skippedOutputBufferCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextOutputBuffer == null) {
|
if (nextOutputBuffer == null) {
|
||||||
|
|
@ -279,26 +281,42 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
||||||
// 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 (isBufferLate(outputBuffer.timeUs - positionUs)) {
|
if (isBufferLate(outputBuffer.timeUs - positionUs)) {
|
||||||
|
forceRenderFrame = false;
|
||||||
skipBuffer();
|
skipBuffer();
|
||||||
|
buffersInCodecCount--;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (forceRenderFrame) {
|
||||||
|
forceRenderFrame = false;
|
||||||
|
renderBuffer();
|
||||||
|
buffersInCodecCount--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
final long nextOutputBufferTimeUs =
|
final long nextOutputBufferTimeUs =
|
||||||
nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||||
? nextOutputBuffer.timeUs : C.TIME_UNSET;
|
? nextOutputBuffer.timeUs : C.TIME_UNSET;
|
||||||
if (shouldDropOutputBuffer(
|
|
||||||
|
long earlyUs = outputBuffer.timeUs - positionUs;
|
||||||
|
if (shouldDropBuffersToKeyframe(earlyUs) && maybeDropBuffersToKeyframe(positionUs)) {
|
||||||
|
forceRenderFrame = true;
|
||||||
|
return false;
|
||||||
|
} else if (shouldDropOutputBuffer(
|
||||||
outputBuffer.timeUs, nextOutputBufferTimeUs, positionUs, joiningDeadlineMs)) {
|
outputBuffer.timeUs, nextOutputBufferTimeUs, positionUs, joiningDeadlineMs)) {
|
||||||
dropBuffer();
|
dropBuffer();
|
||||||
|
buffersInCodecCount--;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have yet to render a frame to the current output (either initially or immediately
|
// If we have yet to render a frame to the current output (either initially or immediately
|
||||||
// following a seek), render one irrespective of the state or current position.
|
// following a seek), render one irrespective of the state or current position.
|
||||||
if (!renderedFirstFrame
|
if (!renderedFirstFrame
|
||||||
|| (getState() == STATE_STARTED && outputBuffer.timeUs <= positionUs + 30000)) {
|
|| (getState() == STATE_STARTED && earlyUs <= 30000)) {
|
||||||
renderBuffer();
|
renderBuffer();
|
||||||
|
buffersInCodecCount--;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -307,18 +325,29 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
* Returns whether the current frame should be dropped.
|
* Returns whether the current frame should be dropped.
|
||||||
*
|
*
|
||||||
* @param outputBufferTimeUs The timestamp of the current output buffer.
|
* @param outputBufferTimeUs The timestamp of the current output buffer.
|
||||||
* @param nextOutputBufferTimeUs The timestamp of the next output buffer or
|
* @param nextOutputBufferTimeUs The timestamp of the next output buffer or {@link C#TIME_UNSET}
|
||||||
* {@link C#TIME_UNSET} if the next output buffer is unavailable.
|
* if the next output buffer is unavailable.
|
||||||
* @param positionUs The current playback position.
|
* @param positionUs The current playback position.
|
||||||
* @param joiningDeadlineMs The joining deadline.
|
* @param joiningDeadlineMs The joining deadline.
|
||||||
* @return Returns whether to drop the current output buffer.
|
* @return Returns whether to drop the current output buffer.
|
||||||
*/
|
*/
|
||||||
protected boolean shouldDropOutputBuffer(long outputBufferTimeUs, long nextOutputBufferTimeUs,
|
private boolean shouldDropOutputBuffer(long outputBufferTimeUs, long nextOutputBufferTimeUs,
|
||||||
long positionUs, long joiningDeadlineMs) {
|
long positionUs, long joiningDeadlineMs) {
|
||||||
return isBufferLate(outputBufferTimeUs - positionUs)
|
return isBufferLate(outputBufferTimeUs - positionUs)
|
||||||
&& (joiningDeadlineMs != C.TIME_UNSET || nextOutputBufferTimeUs != C.TIME_UNSET);
|
&& (joiningDeadlineMs != C.TIME_UNSET || nextOutputBufferTimeUs != C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether to drop all buffers from the buffer being processed to the keyframe at or after
|
||||||
|
* the current playback position, if possible.
|
||||||
|
*
|
||||||
|
* @param earlyUs The time until the current buffer should be presented in microseconds. A
|
||||||
|
* negative value indicates that the buffer is late.
|
||||||
|
*/
|
||||||
|
private boolean shouldDropBuffersToKeyframe(long earlyUs) {
|
||||||
|
return isBufferVeryLate(earlyUs);
|
||||||
|
}
|
||||||
|
|
||||||
private void renderBuffer() {
|
private void renderBuffer() {
|
||||||
int bufferMode = outputBuffer.mode;
|
int bufferMode = outputBuffer.mode;
|
||||||
boolean renderRgb = bufferMode == VpxDecoder.OUTPUT_MODE_RGB && surface != null;
|
boolean renderRgb = bufferMode == VpxDecoder.OUTPUT_MODE_RGB && surface != null;
|
||||||
|
|
@ -342,18 +371,35 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dropBuffer() {
|
private void dropBuffer() {
|
||||||
decoderCounters.droppedBufferCount++;
|
updateDroppedBufferCounters(1);
|
||||||
droppedFrames++;
|
|
||||||
consecutiveDroppedFrameCount++;
|
|
||||||
decoderCounters.maxConsecutiveDroppedBufferCount = Math.max(
|
|
||||||
consecutiveDroppedFrameCount, decoderCounters.maxConsecutiveDroppedBufferCount);
|
|
||||||
if (droppedFrames == maxDroppedFramesToNotify) {
|
|
||||||
maybeNotifyDroppedFrames();
|
|
||||||
}
|
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean maybeDropBuffersToKeyframe(long positionUs) throws ExoPlaybackException {
|
||||||
|
int droppedSourceBufferCount = skipSource(positionUs);
|
||||||
|
if (droppedSourceBufferCount == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decoderCounters.droppedToKeyframeCount++;
|
||||||
|
// We dropped some buffers to catch up, so update the decoder counters and flush the codec,
|
||||||
|
// which releases all pending buffers buffers including the current output buffer.
|
||||||
|
updateDroppedBufferCounters(buffersInCodecCount + droppedSourceBufferCount);
|
||||||
|
flushDecoder();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDroppedBufferCounters(int droppedBufferCount) {
|
||||||
|
decoderCounters.droppedBufferCount += droppedBufferCount;
|
||||||
|
droppedFrames += droppedBufferCount;
|
||||||
|
consecutiveDroppedFrameCount += droppedBufferCount;
|
||||||
|
decoderCounters.maxConsecutiveDroppedBufferCount = Math.max(consecutiveDroppedFrameCount,
|
||||||
|
decoderCounters.maxConsecutiveDroppedBufferCount);
|
||||||
|
if (droppedFrames >= maxDroppedFramesToNotify) {
|
||||||
|
maybeNotifyDroppedFrames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void skipBuffer() {
|
private void skipBuffer() {
|
||||||
decoderCounters.skippedOutputBufferCount++;
|
decoderCounters.skippedOutputBufferCount++;
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
|
|
@ -426,6 +472,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
inputBuffer.flip();
|
inputBuffer.flip();
|
||||||
inputBuffer.colorInfo = formatHolder.format.colorInfo;
|
inputBuffer.colorInfo = formatHolder.format.colorInfo;
|
||||||
decoder.queueInputBuffer(inputBuffer);
|
decoder.queueInputBuffer(inputBuffer);
|
||||||
|
buffersInCodecCount++;
|
||||||
decoderReceivedBuffers = true;
|
decoderReceivedBuffers = true;
|
||||||
decoderCounters.inputBufferCount++;
|
decoderCounters.inputBufferCount++;
|
||||||
inputBuffer = null;
|
inputBuffer = null;
|
||||||
|
|
@ -445,6 +492,8 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
|
|
||||||
private void flushDecoder() throws ExoPlaybackException {
|
private void flushDecoder() throws ExoPlaybackException {
|
||||||
waitingForKeys = false;
|
waitingForKeys = false;
|
||||||
|
forceRenderFrame = false;
|
||||||
|
buffersInCodecCount = 0;
|
||||||
if (decoderReinitializationState != REINITIALIZATION_STATE_NONE) {
|
if (decoderReinitializationState != REINITIALIZATION_STATE_NONE) {
|
||||||
releaseDecoder();
|
releaseDecoder();
|
||||||
maybeInitDecoder();
|
maybeInitDecoder();
|
||||||
|
|
@ -601,6 +650,8 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
decoderCounters.decoderReleaseCount++;
|
decoderCounters.decoderReleaseCount++;
|
||||||
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||||
decoderReceivedBuffers = false;
|
decoderReceivedBuffers = false;
|
||||||
|
forceRenderFrame = false;
|
||||||
|
buffersInCodecCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
private void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
||||||
|
|
@ -735,8 +786,13 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isBufferLate(long earlyUs) {
|
private static boolean isBufferLate(long earlyUs) {
|
||||||
// Class a buffer as late if it should have been presented more than 30ms ago.
|
// Class a buffer as late if it should have been presented more than 30 ms ago.
|
||||||
return earlyUs < -30000;
|
return earlyUs < -30000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isBufferVeryLate(long earlyUs) {
|
||||||
|
// Class a buffer as very late if it should have been presented more than 500 ms ago.
|
||||||
|
return earlyUs < -500000;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -532,6 +532,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
if (surface == dummySurface) {
|
if (surface == dummySurface) {
|
||||||
// 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 (isBufferLate(earlyUs)) {
|
if (isBufferLate(earlyUs)) {
|
||||||
|
forceRenderFrame = false;
|
||||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -569,7 +570,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
if (shouldDropBuffersToKeyframe(earlyUs, elapsedRealtimeUs)
|
if (shouldDropBuffersToKeyframe(earlyUs, elapsedRealtimeUs)
|
||||||
&& maybeDropBuffersToKeyframe(codec, bufferIndex, presentationTimeUs, positionUs)) {
|
&& maybeDropBuffersToKeyframe(codec, bufferIndex, presentationTimeUs, positionUs)) {
|
||||||
forceRenderFrame = true;
|
forceRenderFrame = true;
|
||||||
return true;
|
return false;
|
||||||
} else if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs)) {
|
} else if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs)) {
|
||||||
dropOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
dropOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue