Tweak video renderer surface swaps

Fixes the following issues:

1. When a surface is set, it's expected that the renderer will
notify video size + when a frame is rendered to it. This is true
even if the surface isn't changing. Right now this is achieved
by setting renderedFirstFrame to false, but this is problematic
in the case that the surface isn't changing because (a) it will
force rendering of a subsequent frame to the output even when
paused, which is incorrect, and (b) isReady will return false
until this occurs.

2. When the surface really is changing, isReady can return false
until the first frame has been rendered into the new surface, which
will break seamless surface switching. This change allows isReady
to return true up to allowedJoiningTimeMs for this case.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=154171111
This commit is contained in:
olly 2017-04-25 07:42:03 -07:00 committed by Oliver Woodman
parent e07b8fe7d5
commit fb88087ac4
2 changed files with 98 additions and 25 deletions

View file

@ -464,13 +464,16 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
protected void onPositionReset(long positionUs, boolean joining) {
inputStreamEnded = false;
outputStreamEnded = false;
renderedFirstFrame = false;
clearRenderedFirstFrame();
consecutiveDroppedFrameCount = 0;
if (decoder != null) {
flushDecoder();
}
joiningDeadlineMs = joining && allowedJoiningTimeMs > 0
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
if (joining) {
setJoiningDeadlineMs();
} else {
joiningDeadlineMs = C.TIME_UNSET;
}
}
@Override
@ -492,6 +495,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
format = null;
waitingForKeys = false;
clearReportedVideoSize();
clearRenderedFirstFrame();
try {
releaseDecoder();
} finally {
@ -568,28 +572,44 @@ 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);
// We only need to update the decoder if the output has changed.
if (this.surface != surface || this.outputBufferRenderer != outputBufferRenderer) {
// The output has changed.
this.surface = surface;
this.outputBufferRenderer = outputBufferRenderer;
outputMode = outputBufferRenderer != null ? VpxDecoder.OUTPUT_MODE_YUV
: surface != null ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_NONE;
// 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);
if (outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
if (decoder != null) {
decoder.setOutputMode(outputMode);
}
// If we know the video size, report it again immediately.
maybeRenotifyVideoSizeChanged();
// We haven't rendered to the new output yet.
clearRenderedFirstFrame();
if (getState() == STATE_STARTED) {
setJoiningDeadlineMs();
}
} else {
// The output has been removed. We leave the outputMode of the underlying decoder unchanged
// in anticipation that a subsequent output will likely be of the same type.
clearReportedVideoSize();
clearRenderedFirstFrame();
}
} else if (outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
// The output is unchanged and non-null. If we know the video size and/or have already
// rendered to the output, report these again immediately.
maybeRenotifyVideoSizeChanged();
maybeRenotifyRenderedFirstFrame();
}
// 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 setJoiningDeadlineMs() {
joiningDeadlineMs = allowedJoiningTimeMs > 0
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
}
private void clearRenderedFirstFrame() {
renderedFirstFrame = false;
}
private void maybeNotifyRenderedFirstFrame() {
@ -599,6 +619,17 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
}
}
private void maybeRenotifyRenderedFirstFrame() {
if (renderedFirstFrame) {
eventDispatcher.renderedFirstFrame(surface);
}
}
private void clearReportedVideoSize() {
reportedWidth = Format.NO_VALUE;
reportedHeight = Format.NO_VALUE;
}
private void maybeNotifyVideoSizeChanged(int width, int height) {
if (reportedWidth != width || reportedHeight != height) {
reportedWidth = width;
@ -607,6 +638,12 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
}
}
private void maybeRenotifyVideoSizeChanged() {
if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) {
eventDispatcher.videoSizeChanged(reportedWidth, reportedHeight, 0, 1);
}
}
private void maybeNotifyDroppedFrames() {
if (droppedFrames > 0) {
long now = SystemClock.elapsedRealtime();

View file

@ -166,7 +166,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
currentPixelWidthHeightRatio = Format.NO_VALUE;
pendingPixelWidthHeightRatio = Format.NO_VALUE;
scalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
clearLastReportedVideoSize();
clearReportedVideoSize();
}
@Override
@ -229,8 +229,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
super.onPositionReset(positionUs, joining);
clearRenderedFirstFrame();
consecutiveDroppedFrameCount = 0;
joiningDeadlineMs = joining && allowedJoiningTimeMs > 0
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
if (joining) {
setJoiningDeadlineMs();
} else {
joiningDeadlineMs = C.TIME_UNSET;
}
}
@Override
@ -272,7 +275,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
currentHeight = Format.NO_VALUE;
currentPixelWidthHeightRatio = Format.NO_VALUE;
pendingPixelWidthHeightRatio = Format.NO_VALUE;
clearLastReportedVideoSize();
clearReportedVideoSize();
clearRenderedFirstFrame();
frameReleaseTimeHelper.disable();
tunnelingOnFrameRenderedListener = null;
try {
@ -312,11 +316,25 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
maybeInitCodec();
}
}
if (surface != null) {
// If we know the video size, report it again immediately.
maybeRenotifyVideoSizeChanged();
// We haven't rendered to the new surface yet.
clearRenderedFirstFrame();
if (state == STATE_STARTED) {
setJoiningDeadlineMs();
}
} else {
// The surface has been removed.
clearReportedVideoSize();
clearRenderedFirstFrame();
}
} else if (surface != null) {
// The surface is unchanged and non-null. If we know the video size and/or have already
// rendered to the surface, report these again immediately.
maybeRenotifyVideoSizeChanged();
maybeRenotifyRenderedFirstFrame();
}
// Clear state so that we always call the event listener with the video size and when a frame
// is rendered, even if the surface hasn't changed.
clearRenderedFirstFrame();
clearLastReportedVideoSize();
}
@Override
@ -521,6 +539,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
maybeNotifyRenderedFirstFrame();
}
private void setJoiningDeadlineMs() {
joiningDeadlineMs = allowedJoiningTimeMs > 0
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
}
private void clearRenderedFirstFrame() {
renderedFirstFrame = false;
// The first frame notification is triggered by renderOutputBuffer or renderOutputBufferV21 for
@ -543,7 +566,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
private void clearLastReportedVideoSize() {
private void maybeRenotifyRenderedFirstFrame() {
if (renderedFirstFrame) {
eventDispatcher.renderedFirstFrame(surface);
}
}
private void clearReportedVideoSize() {
reportedWidth = Format.NO_VALUE;
reportedHeight = Format.NO_VALUE;
reportedPixelWidthHeightRatio = Format.NO_VALUE;
@ -563,6 +592,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
private void maybeRenotifyVideoSizeChanged() {
if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) {
eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees,
currentPixelWidthHeightRatio);
}
}
private void maybeNotifyDroppedFrames() {
if (droppedFrames > 0) {
long now = SystemClock.elapsedRealtime();