From 15c2f9c32871857a8665bd2b1badc2f1d287bc0d Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Mon, 28 Sep 2015 12:38:35 +0100 Subject: [PATCH] Explicitly set max input size for H264 decoders. This works around an issue where some devices, such as the Acer Iconia, don't allocate large enough input buffers for H264. Issue: #616 Issue: #714 --- .../MediaCodecAudioTrackRenderer.java | 2 +- .../exoplayer/MediaCodecTrackRenderer.java | 20 +++++++------- .../MediaCodecVideoTrackRenderer.java | 27 ++++++++++++++++++- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java index 79ff8c4715..8a9f2881ba 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java @@ -196,7 +196,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem } @Override - protected void configureCodec(MediaCodec codec, String codecName, + protected void configureCodec(MediaCodec codec, String codecName, boolean codecIsAdaptive, android.media.MediaFormat format, android.media.MediaCrypto crypto) { String mimeType = format.getString(android.media.MediaFormat.KEY_MIME); if (RAW_DECODER_NAME.equals(codecName) && !MimeTypes.AUDIO_RAW.equals(mimeType)) { diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index ef47425711..6f92d0dc56 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -269,10 +269,11 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer * * @param codec The {@link MediaCodec} to configure. * @param codecName The name of the codec. + * @param codecIsAdaptive Whether the codec is adaptive. * @param format The format for which the codec is being configured. * @param crypto For drm protected playbacks, a {@link MediaCrypto} to use for decryption. */ - protected void configureCodec(MediaCodec codec, String codecName, + protected void configureCodec(MediaCodec codec, String codecName, boolean codecIsAdaptive, android.media.MediaFormat format, MediaCrypto crypto) { codec.configure(format, null, crypto, 0); } @@ -320,28 +321,29 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer DecoderInitializationException.NO_SUITABLE_DECODER_ERROR)); } - String decoderName = decoderInfo.name; + String codecName = decoderInfo.name; codecIsAdaptive = decoderInfo.adaptive; - codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(decoderName); - codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(decoderName); + codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(codecName); + codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName); try { long codecInitializingTimestamp = SystemClock.elapsedRealtime(); - TraceUtil.beginSection("createByCodecName(" + decoderName + ")"); - codec = MediaCodec.createByCodecName(decoderName); + TraceUtil.beginSection("createByCodecName(" + codecName + ")"); + codec = MediaCodec.createByCodecName(codecName); TraceUtil.endSection(); TraceUtil.beginSection("configureCodec"); - configureCodec(codec, decoderName, format.getFrameworkMediaFormatV16(), mediaCrypto); + configureCodec(codec, codecName, codecIsAdaptive, format.getFrameworkMediaFormatV16(), + mediaCrypto); TraceUtil.endSection(); TraceUtil.beginSection("codec.start()"); codec.start(); TraceUtil.endSection(); long codecInitializedTimestamp = SystemClock.elapsedRealtime(); - notifyDecoderInitialized(decoderName, codecInitializedTimestamp, + notifyDecoderInitialized(codecName, codecInitializedTimestamp, codecInitializedTimestamp - codecInitializingTimestamp); inputBuffers = codec.getInputBuffers(); outputBuffers = codec.getOutputBuffers(); } catch (Exception e) { - notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, decoderName)); + notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, codecName)); } codecHotswapTimeMs = getState() == TrackRenderer.STATE_STARTED ? SystemClock.elapsedRealtime() : -1; diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java index 28dccafcb0..6cf315bdac 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java @@ -21,6 +21,7 @@ import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.TraceUtil; import com.google.android.exoplayer.util.Util; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.media.MediaCodec; import android.media.MediaCrypto; @@ -378,8 +379,9 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { // Override configureCodec to provide the surface. @Override - protected void configureCodec(MediaCodec codec, String codecName, + protected void configureCodec(MediaCodec codec, String codecName, boolean codecIsAdaptive, android.media.MediaFormat format, MediaCrypto crypto) { + maybeSetMaxInputSize(format, codecIsAdaptive); codec.configure(format, surface, crypto, 0); codec.setVideoScalingMode(videoScalingMode); } @@ -548,6 +550,29 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { maybeNotifyDrawnToSurface(); } + @SuppressLint("InlinedApi") + private void maybeSetMaxInputSize(android.media.MediaFormat format, boolean codecIsAdaptive) { + if (!MimeTypes.VIDEO_H264.equals(format.getString(android.media.MediaFormat.KEY_MIME))) { + // Only set a max input size for H264 for now. + return; + } + if (format.containsKey(android.media.MediaFormat.KEY_MAX_INPUT_SIZE)) { + // Already set. The source of the format may know better, so do nothing. + return; + } + int maxHeight = format.getInteger(android.media.MediaFormat.KEY_HEIGHT); + if (codecIsAdaptive && format.containsKey(android.media.MediaFormat.KEY_MAX_HEIGHT)) { + maxHeight = Math.max(maxHeight, format.getInteger(android.media.MediaFormat.KEY_MAX_HEIGHT)); + } + int maxWidth = format.getInteger(android.media.MediaFormat.KEY_WIDTH); + if (codecIsAdaptive && format.containsKey(android.media.MediaFormat.KEY_MAX_WIDTH)) { + maxWidth = Math.max(maxHeight, format.getInteger(android.media.MediaFormat.KEY_MAX_WIDTH)); + } + // H264 requires compression ratio of at least 2, and uses macroblocks. + int maxInputSize = ((maxWidth + 15) / 16) * ((maxHeight + 15) / 16) * 192; + format.setInteger(android.media.MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize); + } + private void maybeNotifyVideoSizeChanged() { if (eventHandler == null || eventListener == null || (lastReportedWidth == currentWidth && lastReportedHeight == currentHeight