mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix VP9 extension surface attach/detach + make it seamless
Issue: #2582 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=152494408
This commit is contained in:
parent
4b94877476
commit
cf4358b8fa
1 changed files with 93 additions and 97 deletions
|
|
@ -186,7 +186,6 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have a format.
|
// We have a format.
|
||||||
if (isRendererAvailable()) {
|
|
||||||
drmSession = pendingDrmSession;
|
drmSession = pendingDrmSession;
|
||||||
ExoMediaCrypto mediaCrypto = null;
|
ExoMediaCrypto mediaCrypto = null;
|
||||||
if (drmSession != null) {
|
if (drmSession != null) {
|
||||||
|
|
@ -206,8 +205,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
// If we don't have a decoder yet, we need to instantiate one.
|
// If we don't have a decoder yet, we need to instantiate one.
|
||||||
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||||
TraceUtil.beginSection("createVpxDecoder");
|
TraceUtil.beginSection("createVpxDecoder");
|
||||||
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE,
|
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE, mediaCrypto);
|
||||||
mediaCrypto);
|
|
||||||
decoder.setOutputMode(outputMode);
|
decoder.setOutputMode(outputMode);
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||||
|
|
@ -222,22 +220,6 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
} catch (VpxDecoderException e) {
|
} catch (VpxDecoderException e) {
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
skipSource(positionUs);
|
|
||||||
// We need to read any format changes despite not having a codec so that drmSession can be
|
|
||||||
// updated, and so that we have the most recent format should the codec be initialized. We may
|
|
||||||
// also reach the end of the stream. Note that readSource will not read a sample into a
|
|
||||||
// flags-only buffer.
|
|
||||||
flagsOnlyBuffer.clear();
|
|
||||||
int result = readSource(formatHolder, flagsOnlyBuffer, false);
|
|
||||||
if (result == C.RESULT_FORMAT_READ) {
|
|
||||||
onInputFormatChanged(formatHolder.format);
|
|
||||||
} else if (result == C.RESULT_BUFFER_READ) {
|
|
||||||
Assertions.checkState(flagsOnlyBuffer.isEndOfStream());
|
|
||||||
inputStreamEnded = true;
|
|
||||||
outputStreamEnded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decoderCounters.ensureUpdated();
|
decoderCounters.ensureUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,27 +253,26 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
||||||
|
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
||||||
|
if (outputBuffer.timeUs <= positionUs) {
|
||||||
|
skipBuffer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
||||||
// and that's also late. Else we'll render what we have.
|
// and that's also late. Else we'll render what we have.
|
||||||
if ((joiningDeadlineMs != C.TIME_UNSET && outputBuffer.timeUs < positionUs - 30000)
|
if ((joiningDeadlineMs != C.TIME_UNSET && outputBuffer.timeUs < positionUs - 30000)
|
||||||
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||||
&& nextOutputBuffer.timeUs < positionUs)) {
|
&& nextOutputBuffer.timeUs < positionUs)) {
|
||||||
decoderCounters.droppedOutputBufferCount++;
|
dropBuffer();
|
||||||
droppedFrames++;
|
|
||||||
consecutiveDroppedFrameCount++;
|
|
||||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(
|
|
||||||
consecutiveDroppedFrameCount,
|
|
||||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount);
|
|
||||||
if (droppedFrames == maxDroppedFramesToNotify) {
|
|
||||||
maybeNotifyDroppedFrames();
|
|
||||||
}
|
|
||||||
outputBuffer.release();
|
|
||||||
outputBuffer = null;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have not rendered any frame so far (either initially or immediately following a seek),
|
// If we have yet to render a frame to the current output (either initially or immediately
|
||||||
// render one frame 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 && outputBuffer.timeUs <= positionUs + 30000)) {
|
||||||
renderBuffer();
|
renderBuffer();
|
||||||
|
|
@ -300,27 +281,44 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderBuffer() {
|
private void renderBuffer() {
|
||||||
decoderCounters.renderedOutputBufferCount++;
|
int bufferMode = outputBuffer.mode;
|
||||||
consecutiveDroppedFrameCount = 0;
|
boolean renderRgb = bufferMode == VpxDecoder.OUTPUT_MODE_RGB && surface != null;
|
||||||
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
boolean renderYuv = bufferMode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null;
|
||||||
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
if (!renderRgb && !renderYuv) {
|
||||||
renderRgbFrame(outputBuffer, scaleToFit);
|
dropBuffer();
|
||||||
if (!renderedFirstFrame) {
|
|
||||||
renderedFirstFrame = true;
|
|
||||||
eventDispatcher.renderedFirstFrame(surface);
|
|
||||||
}
|
|
||||||
outputBuffer.release();
|
|
||||||
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
|
||||||
// The renderer will release the buffer.
|
|
||||||
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
|
||||||
if (!renderedFirstFrame) {
|
|
||||||
renderedFirstFrame = true;
|
|
||||||
eventDispatcher.renderedFirstFrame(null);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||||
|
if (renderRgb) {
|
||||||
|
renderRgbFrame(outputBuffer, scaleToFit);
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
|
} else /* renderYuv */ {
|
||||||
|
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
||||||
|
// The renderer will release the buffer.
|
||||||
}
|
}
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
|
consecutiveDroppedFrameCount = 0;
|
||||||
|
decoderCounters.renderedOutputBufferCount++;
|
||||||
|
maybeNotifyRenderedFirstFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dropBuffer() {
|
||||||
|
decoderCounters.droppedOutputBufferCount++;
|
||||||
|
droppedFrames++;
|
||||||
|
consecutiveDroppedFrameCount++;
|
||||||
|
decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(
|
||||||
|
consecutiveDroppedFrameCount, decoderCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||||
|
if (droppedFrames == maxDroppedFramesToNotify) {
|
||||||
|
maybeNotifyDroppedFrames();
|
||||||
|
}
|
||||||
|
outputBuffer.release();
|
||||||
|
outputBuffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipBuffer() {
|
||||||
|
decoderCounters.skippedOutputBufferCount++;
|
||||||
|
outputBuffer.release();
|
||||||
|
outputBuffer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderRgbFrame(VpxOutputBuffer outputBuffer, boolean scale) {
|
private void renderRgbFrame(VpxOutputBuffer outputBuffer, boolean scale) {
|
||||||
|
|
@ -420,7 +418,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (format != null && (isSourceReady() || outputBuffer != null)
|
if (format != null && (isSourceReady() || outputBuffer != null)
|
||||||
&& (renderedFirstFrame || !isRendererAvailable())) {
|
&& (renderedFirstFrame || outputMode == VpxDecoder.OUTPUT_MODE_NONE)) {
|
||||||
// Ready. If we were joining then we've now joined, so clear the joining deadline.
|
// Ready. If we were joining then we've now joined, so clear the joining deadline.
|
||||||
joiningDeadlineMs = C.TIME_UNSET;
|
joiningDeadlineMs = C.TIME_UNSET;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -551,32 +549,23 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
private void setOutput(Surface surface, VpxOutputBufferRenderer outputBufferRenderer) {
|
private void setOutput(Surface surface, VpxOutputBufferRenderer outputBufferRenderer) {
|
||||||
// At most one output may be non-null. Both may be null if the output is being cleared.
|
// At most one output may be non-null. Both may be null if the output is being cleared.
|
||||||
Assertions.checkState(surface == null || outputBufferRenderer == null);
|
Assertions.checkState(surface == null || outputBufferRenderer == null);
|
||||||
// Clear state so that we always call the event listener with the video size and when a frame
|
|
||||||
// is rendered, even if the output hasn't changed.
|
|
||||||
renderedFirstFrame = false;
|
|
||||||
clearReportedVideoSize();
|
|
||||||
// We only need to update the decoder if the output has changed.
|
// We only need to update the decoder if the output has changed.
|
||||||
if (this.surface != surface || this.outputBufferRenderer != outputBufferRenderer) {
|
if (this.surface != surface || this.outputBufferRenderer != outputBufferRenderer) {
|
||||||
this.surface = surface;
|
this.surface = surface;
|
||||||
this.outputBufferRenderer = outputBufferRenderer;
|
this.outputBufferRenderer = outputBufferRenderer;
|
||||||
outputMode = outputBufferRenderer != null ? VpxDecoder.OUTPUT_MODE_YUV
|
outputMode = outputBufferRenderer != null ? VpxDecoder.OUTPUT_MODE_YUV
|
||||||
: surface != null ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_NONE;
|
: surface != null ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_NONE;
|
||||||
updateDecoder();
|
// If outputMode is OUTPUT_MODE_NONE we leave the mode of the underlying decoder unchanged in
|
||||||
}
|
// anticipation that a subsequent output will likely be of the same type as the one that was
|
||||||
}
|
// set previously.
|
||||||
|
if (decoder != null && outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
|
||||||
private void updateDecoder() {
|
|
||||||
if (decoder != null) {
|
|
||||||
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
|
||||||
releaseDecoder();
|
|
||||||
} else {
|
|
||||||
decoder.setOutputMode(outputMode);
|
decoder.setOutputMode(outputMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Clear state so that we always call the event listener with the video size and when a frame
|
||||||
|
// is rendered, even if the output hasn't changed.
|
||||||
private boolean isRendererAvailable() {
|
renderedFirstFrame = false;
|
||||||
return surface != null || outputBufferRenderer != null;
|
clearReportedVideoSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearReportedVideoSize() {
|
private void clearReportedVideoSize() {
|
||||||
|
|
@ -584,6 +573,13 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||||
reportedHeight = Format.NO_VALUE;
|
reportedHeight = Format.NO_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeNotifyRenderedFirstFrame() {
|
||||||
|
if (!renderedFirstFrame) {
|
||||||
|
renderedFirstFrame = true;
|
||||||
|
eventDispatcher.renderedFirstFrame(surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void maybeNotifyVideoSizeChanged(int width, int height) {
|
private void maybeNotifyVideoSizeChanged(int width, int height) {
|
||||||
if (reportedWidth != width || reportedHeight != height) {
|
if (reportedWidth != width || reportedHeight != height) {
|
||||||
reportedWidth = width;
|
reportedWidth = width;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue