From 03fe503f1c4744848a1a2a68950b96d81c2f845c Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 20 Sep 2018 09:48:44 -0700 Subject: [PATCH] Propagate output format in tunneling mode From API 23 this uses the timed format queue. Before API 23 the format is notified as soon as the buffer is queued. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=213830729 --- .../mediacodec/MediaCodecRenderer.java | 19 +++-- .../video/MediaCodecVideoRenderer.java | 71 ++++++++++++------- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 54c2b23c3b..7e933e9474 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -497,6 +497,20 @@ public abstract class MediaCodecRenderer extends BaseRenderer { return false; } + /** + * Polls the pending output format queue for a given buffer timestamp. If a format is present, it + * is removed and returned. Otherwise returns {@code null}. Subclasses should only call this + * method if they are taking over responsibility for output format propagation (e.g., when using + * video tunneling). + */ + protected final @Nullable Format updateOutputFormatForTime(long presentationTimeUs) { + Format format = formatQueue.pollFloor(presentationTimeUs); + if (format != null) { + outputFormat = format; + } + return format; + } + protected final MediaCodec getCodec() { return codec; } @@ -1297,10 +1311,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size); } shouldSkipOutputBuffer = shouldSkipOutputBuffer(outputBufferInfo.presentationTimeUs); - Format format = formatQueue.pollFloor(outputBufferInfo.presentationTimeUs); - if (format != null) { - outputFormat = format; - } + updateOutputFormatForTime(outputBufferInfo.presentationTimeUs); } boolean processedOutputBuffer; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 35121829a3..d217b47785 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -566,7 +566,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { buffersInCodecCount++; lastInputTimeUs = Math.max(buffer.timeUs, lastInputTimeUs); if (Util.SDK_INT < 23 && tunneling) { - maybeNotifyRenderedFirstFrame(); + // In tunneled mode before API 23 we don't have a way to know when the buffer is output, so + // treat it as if it were output immediately. + onProcessedTunneledBuffer(buffer.timeUs); } } @@ -575,29 +577,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT) && outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM) && outputFormat.containsKey(KEY_CROP_TOP); - currentWidth = hasCrop - ? outputFormat.getInteger(KEY_CROP_RIGHT) - outputFormat.getInteger(KEY_CROP_LEFT) + 1 - : outputFormat.getInteger(MediaFormat.KEY_WIDTH); - currentHeight = hasCrop - ? outputFormat.getInteger(KEY_CROP_BOTTOM) - outputFormat.getInteger(KEY_CROP_TOP) + 1 - : outputFormat.getInteger(MediaFormat.KEY_HEIGHT); - currentPixelWidthHeightRatio = pendingPixelWidthHeightRatio; - if (Util.SDK_INT >= 21) { - // On API level 21 and above the decoder applies the rotation when rendering to the surface. - // Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need - // to flip the width, height and pixel aspect ratio to reflect the rotation that was applied. - if (pendingRotationDegrees == 90 || pendingRotationDegrees == 270) { - int rotatedHeight = currentWidth; - currentWidth = currentHeight; - currentHeight = rotatedHeight; - currentPixelWidthHeightRatio = 1 / currentPixelWidthHeightRatio; - } - } else { - // On API level 20 and below the decoder does not apply the rotation. - currentUnappliedRotationDegrees = pendingRotationDegrees; - } - // Must be applied each time the output format changes. - codec.setVideoScalingMode(scalingMode); + int width = + hasCrop + ? outputFormat.getInteger(KEY_CROP_RIGHT) - outputFormat.getInteger(KEY_CROP_LEFT) + 1 + : outputFormat.getInteger(MediaFormat.KEY_WIDTH); + int height = + hasCrop + ? outputFormat.getInteger(KEY_CROP_BOTTOM) - outputFormat.getInteger(KEY_CROP_TOP) + 1 + : outputFormat.getInteger(MediaFormat.KEY_HEIGHT); + processOutputFormat(codec, width, height); } @Override @@ -705,6 +693,28 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { return false; } + private void processOutputFormat(MediaCodec codec, int width, int height) { + currentWidth = width; + currentHeight = height; + currentPixelWidthHeightRatio = pendingPixelWidthHeightRatio; + if (Util.SDK_INT >= 21) { + // On API level 21 and above the decoder applies the rotation when rendering to the surface. + // Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need + // to flip the width, height and pixel aspect ratio to reflect the rotation that was applied. + if (pendingRotationDegrees == 90 || pendingRotationDegrees == 270) { + int rotatedHeight = currentWidth; + currentWidth = currentHeight; + currentHeight = rotatedHeight; + currentPixelWidthHeightRatio = 1 / currentPixelWidthHeightRatio; + } + } else { + // On API level 20 and below the decoder does not apply the rotation. + currentUnappliedRotationDegrees = pendingRotationDegrees; + } + // Must be applied each time the output format changes. + codec.setVideoScalingMode(scalingMode); + } + private void notifyFrameMetadataListener( long presentationTimeUs, long releaseTimeNs, Format format) { if (frameMetadataListener != null) { @@ -722,6 +732,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { return outputStreamOffsetUs; } + /** Called when a buffer was processed in tunneling mode. */ + protected void onProcessedTunneledBuffer(long presentationTimeUs) { + @Nullable Format format = updateOutputFormatForTime(presentationTimeUs); + if (format != null) { + processOutputFormat(getCodec(), format.width, format.height); + } + maybeNotifyVideoSizeChanged(); + maybeNotifyRenderedFirstFrame(); + onProcessedOutputBuffer(presentationTimeUs); + } + /** * Called when an output buffer is successfully processed. * @@ -1463,7 +1484,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { // Stale event. return; } - maybeNotifyRenderedFirstFrame(); + onProcessedTunneledBuffer(presentationTimeUs); } }