Commit cf4358b8 by olly Committed by Oliver Woodman

Fix VP9 extension surface attach/detach + make it seamless

Issue: #2582

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=152494408
parent 4b948774
...@@ -186,7 +186,6 @@ public final class LibvpxVideoRenderer extends BaseRenderer { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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,26 +281,43 @@ public final class LibvpxVideoRenderer extends BaseRenderer { ...@@ -300,26 +281,43 @@ 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;
boolean renderYuv = bufferMode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null;
if (!renderRgb && !renderYuv) {
dropBuffer();
} else {
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height); maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) { if (renderRgb) {
renderRgbFrame(outputBuffer, scaleToFit); renderRgbFrame(outputBuffer, scaleToFit);
if (!renderedFirstFrame) {
renderedFirstFrame = true;
eventDispatcher.renderedFirstFrame(surface);
}
outputBuffer.release(); outputBuffer.release();
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) { } else /* renderYuv */ {
// The renderer will release the buffer.
outputBufferRenderer.setOutputBuffer(outputBuffer); outputBufferRenderer.setOutputBuffer(outputBuffer);
if (!renderedFirstFrame) { // The renderer will release the buffer.
renderedFirstFrame = true; }
eventDispatcher.renderedFirstFrame(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();
} }
} else {
outputBuffer.release(); outputBuffer.release();
outputBuffer = null;
} }
private void skipBuffer() {
decoderCounters.skippedOutputBufferCount++;
outputBuffer.release();
outputBuffer = null; outputBuffer = null;
} }
...@@ -420,7 +418,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer { ...@@ -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 { ...@@ -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 { ...@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment