Skip to content

Commit

Permalink
Fix VP9 extension surface attach/detach + make it seamless
Browse files Browse the repository at this point in the history
Issue: #2582

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=152494408
  • Loading branch information
ojw28 committed Apr 11, 2017
1 parent 4b94877 commit cf4358b
Showing 1 changed file with 93 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,57 +186,39 @@ public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackEx
}

// 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();
}
Expand Down Expand Up @@ -271,27 +253,26 @@ private boolean drainOutputBuffer(long positionUs) throws VpxDecoderException {
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();
Expand All @@ -300,26 +281,43 @@ private boolean drainOutputBuffer(long positionUs) throws VpxDecoderException {
}

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;
}

Expand Down Expand Up @@ -420,7 +418,7 @@ public boolean isReady() {
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;
Expand Down Expand Up @@ -551,39 +549,37 @@ public void handleMessage(int messageType, Object message) throws ExoPlaybackExc
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() {
reportedWidth = Format.NO_VALUE;
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;
Expand Down

0 comments on commit cf4358b

Please sign in to comment.