mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +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,57 +186,39 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
// We have a format.
|
||||
if (isRendererAvailable()) {
|
||||
drmSession = pendingDrmSession;
|
||||
ExoMediaCrypto mediaCrypto = null;
|
||||
if (drmSession != null) {
|
||||
int drmSessionState = drmSession.getState();
|
||||
if (drmSessionState == DrmSession.STATE_ERROR) {
|
||||
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
|
||||
} else if (drmSessionState == DrmSession.STATE_OPENED
|
||||
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) {
|
||||
mediaCrypto = drmSession.getMediaCrypto();
|
||||
} else {
|
||||
// The drm session isn't open yet.
|
||||
return;
|
||||
}
|
||||
drmSession = pendingDrmSession;
|
||||
ExoMediaCrypto mediaCrypto = null;
|
||||
if (drmSession != null) {
|
||||
int drmSessionState = drmSession.getState();
|
||||
if (drmSessionState == DrmSession.STATE_ERROR) {
|
||||
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
|
||||
} else if (drmSessionState == DrmSession.STATE_OPENED
|
||||
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) {
|
||||
mediaCrypto = drmSession.getMediaCrypto();
|
||||
} else {
|
||||
// The drm session isn't open yet.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (decoder == null) {
|
||||
// If we don't have a decoder yet, we need to instantiate one.
|
||||
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||
TraceUtil.beginSection("createVpxDecoder");
|
||||
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE,
|
||||
mediaCrypto);
|
||||
decoder.setOutputMode(outputMode);
|
||||
TraceUtil.endSection();
|
||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||
decoderCounters.decoderInitCount++;
|
||||
}
|
||||
TraceUtil.beginSection("drainAndFeed");
|
||||
while (drainOutputBuffer(positionUs)) {}
|
||||
while (feedInputBuffer()) {}
|
||||
}
|
||||
try {
|
||||
if (decoder == null) {
|
||||
// If we don't have a decoder yet, we need to instantiate one.
|
||||
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||
TraceUtil.beginSection("createVpxDecoder");
|
||||
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE, mediaCrypto);
|
||||
decoder.setOutputMode(outputMode);
|
||||
TraceUtil.endSection();
|
||||
} catch (VpxDecoderException e) {
|
||||
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;
|
||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||
decoderCounters.decoderInitCount++;
|
||||
}
|
||||
TraceUtil.beginSection("drainAndFeed");
|
||||
while (drainOutputBuffer(positionUs)) {}
|
||||
while (feedInputBuffer()) {}
|
||||
TraceUtil.endSection();
|
||||
} catch (VpxDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
decoderCounters.ensureUpdated();
|
||||
}
|
||||
|
|
@ -271,27 +253,26 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
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
|
||||
// and that's also late. Else we'll render what we have.
|
||||
if ((joiningDeadlineMs != C.TIME_UNSET && outputBuffer.timeUs < positionUs - 30000)
|
||||
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||
&& nextOutputBuffer.timeUs < positionUs)) {
|
||||
decoderCounters.droppedOutputBufferCount++;
|
||||
droppedFrames++;
|
||||
consecutiveDroppedFrameCount++;
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(
|
||||
consecutiveDroppedFrameCount,
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
if (droppedFrames == maxDroppedFramesToNotify) {
|
||||
maybeNotifyDroppedFrames();
|
||||
}
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
dropBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have not rendered any frame so far (either initially or immediately following a seek),
|
||||
// render one frame irrespective of the state or current position.
|
||||
// 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.
|
||||
if (!renderedFirstFrame
|
||||
|| (getState() == STATE_STARTED && outputBuffer.timeUs <= positionUs + 30000)) {
|
||||
renderBuffer();
|
||||
|
|
@ -300,26 +281,43 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
private void renderBuffer() {
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
||||
renderRgbFrame(outputBuffer, scaleToFit);
|
||||
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);
|
||||
}
|
||||
int bufferMode = outputBuffer.mode;
|
||||
boolean renderRgb = bufferMode == VpxDecoder.OUTPUT_MODE_RGB && surface != null;
|
||||
boolean renderYuv = bufferMode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null;
|
||||
if (!renderRgb && !renderYuv) {
|
||||
dropBuffer();
|
||||
} else {
|
||||
outputBuffer.release();
|
||||
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||
if (renderRgb) {
|
||||
renderRgbFrame(outputBuffer, scaleToFit);
|
||||
outputBuffer.release();
|
||||
} else /* renderYuv */ {
|
||||
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
||||
// The renderer will release the buffer.
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -420,7 +418,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
return false;
|
||||
}
|
||||
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.
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
return true;
|
||||
|
|
@ -551,32 +549,23 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
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.
|
||||
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.
|
||||
if (this.surface != surface || this.outputBufferRenderer != outputBufferRenderer) {
|
||||
this.surface = surface;
|
||||
this.outputBufferRenderer = outputBufferRenderer;
|
||||
outputMode = outputBufferRenderer != null ? VpxDecoder.OUTPUT_MODE_YUV
|
||||
: surface != null ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_NONE;
|
||||
updateDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDecoder() {
|
||||
if (decoder != null) {
|
||||
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
releaseDecoder();
|
||||
} else {
|
||||
// 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) {
|
||||
decoder.setOutputMode(outputMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRendererAvailable() {
|
||||
return 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();
|
||||
}
|
||||
|
||||
private void clearReportedVideoSize() {
|
||||
|
|
@ -584,6 +573,13 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
reportedHeight = Format.NO_VALUE;
|
||||
}
|
||||
|
||||
private void maybeNotifyRenderedFirstFrame() {
|
||||
if (!renderedFirstFrame) {
|
||||
renderedFirstFrame = true;
|
||||
eventDispatcher.renderedFirstFrame(surface);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeNotifyVideoSizeChanged(int width, int height) {
|
||||
if (reportedWidth != width || reportedHeight != height) {
|
||||
reportedWidth = width;
|
||||
|
|
|
|||
Loading…
Reference in a new issue