From 2ab67380d68097960fb061ffdb3e0f57a6ffc888 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 9 Mar 2015 01:49:52 -0500 Subject: [PATCH] Use direct submit decoding for MediaCodec. Based on my profiling of a few devices, dequeueInputBuffer and queueInputBuffer don't take much time anyway. It allows us to stop our semi-busy looping which saves power. The depacketizer can avoid expensive synchronization and additional context switching which costs time and CPU cycles. --- app/libs/limelight-common.jar | Bin 953516 -> 956588 bytes .../video/ConfigurableDecoderRenderer.java | 6 + .../video/EnhancedDecoderRenderer.java | 2 +- .../video/MediaCodecDecoderRenderer.java | 191 +++++------------- 4 files changed, 56 insertions(+), 143 deletions(-) diff --git a/app/libs/limelight-common.jar b/app/libs/limelight-common.jar index 5ff38374009fdec43afafab5a299efa1a03d3318..530e78dbc71e458b945c9a69cce646c44eb4852a 100644 GIT binary patch delta 22993 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 delta 20107 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�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 diff --git a/app/src/main/java/com/limelight/binding/video/ConfigurableDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/ConfigurableDecoderRenderer.java index 071b2294..f65830ed 100644 --- a/app/src/main/java/com/limelight/binding/video/ConfigurableDecoderRenderer.java +++ b/app/src/main/java/com/limelight/binding/video/ConfigurableDecoderRenderer.java @@ -1,5 +1,6 @@ package com.limelight.binding.video; +import com.limelight.nvstream.av.DecodeUnit; import com.limelight.nvstream.av.video.VideoDecoderRenderer; import com.limelight.nvstream.av.video.VideoDepacketizer; @@ -55,6 +56,11 @@ public class ConfigurableDecoderRenderer extends EnhancedDecoderRenderer { return decoderRenderer.getCapabilities(); } + @Override + public void directSubmitDecodeUnit(DecodeUnit du) { + decoderRenderer.directSubmitDecodeUnit(du); + } + @Override public int getAverageDecoderLatency() { if (decoderRenderer != null) { diff --git a/app/src/main/java/com/limelight/binding/video/EnhancedDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/EnhancedDecoderRenderer.java index 56392d89..5343c381 100644 --- a/app/src/main/java/com/limelight/binding/video/EnhancedDecoderRenderer.java +++ b/app/src/main/java/com/limelight/binding/video/EnhancedDecoderRenderer.java @@ -2,6 +2,6 @@ package com.limelight.binding.video; import com.limelight.nvstream.av.video.VideoDecoderRenderer; -public abstract class EnhancedDecoderRenderer implements VideoDecoderRenderer { +public abstract class EnhancedDecoderRenderer extends VideoDecoderRenderer { public abstract String getDecoderName(); } diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java index 5a24de9b..795efc06 100644 --- a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java +++ b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -2,7 +2,6 @@ package com.limelight.binding.video; import java.nio.ByteBuffer; import java.util.Locale; -import java.util.concurrent.locks.LockSupport; import org.jcodec.codecs.h264.io.model.SeqParameterSet; import org.jcodec.codecs.h264.io.model.VUIParameters; @@ -46,8 +45,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { private int numPpsIn; private int numIframeIn; - private static final boolean ENABLE_ASYNC_RENDERER = false; - @TargetApi(Build.VERSION_CODES.KITKAT) public MediaCodecDecoderRenderer() { //dumpDecoders(); @@ -79,7 +76,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { } } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public boolean setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) { this.initialWidth = width; @@ -107,52 +103,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, height); } - // On Lollipop, we use asynchronous mode to avoid having a busy looping renderer thread - if (ENABLE_ASYNC_RENDERER && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - videoDecoder.setCallback(new MediaCodec.Callback() { - @Override - public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { - LimeLog.info("Output format changed"); - LimeLog.info("New output Format: " + format); - } - - @Override - public void onOutputBufferAvailable(MediaCodec codec, int index, - BufferInfo info) { - try { - // FIXME: It looks like we can't frameskip here - codec.releaseOutputBuffer(index, true); - } catch (Exception e) { - handleDecoderException(MediaCodecDecoderRenderer.this, e, null, 0); - } - } - - @Override - public void onInputBufferAvailable(MediaCodec codec, int index) { - try { - submitDecodeUnit(depacketizer.takeNextDecodeUnit(), codec.getInputBuffer(index), index); - } catch (InterruptedException e) { - // What do we do here? - e.printStackTrace(); - } catch (Exception e) { - handleDecoderException(MediaCodecDecoderRenderer.this, e, null, 0); - } - } - - @Override - public void onError(MediaCodec codec, CodecException e) { - if (e.isTransient()) { - LimeLog.warning(e.getDiagnosticInfo()); - e.printStackTrace(); - } - else { - LimeLog.severe(e.getDiagnosticInfo()); - e.printStackTrace(); - } - } - }); - } - videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0); videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); @@ -162,7 +112,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { } @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void handleDecoderException(MediaCodecDecoderRenderer dr, Exception e, ByteBuffer buf, int codecFlags) { + private void handleDecoderException(Exception e, ByteBuffer buf, int codecFlags) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (e instanceof CodecException) { CodecException codecExc = (CodecException) e; @@ -178,10 +128,10 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { } if (buf != null || codecFlags != 0) { - throw new RendererException(dr, e, buf, codecFlags); + throw new RendererException(this, e, buf, codecFlags); } else { - throw new RendererException(dr, e); + throw new RendererException(this, e); } } @@ -192,71 +142,10 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { @Override public void run() { BufferInfo info = new BufferInfo(); - DecodeUnit du = null; - int inputIndex = -1; - while (!isInterrupted()) - { - // In order to get as much data to the decoder as early as possible, - // try to submit up to 5 decode units at once without blocking. - if (inputIndex == -1 && du == null) { - try { - for (int i = 0; i < 5; i++) { - inputIndex = videoDecoder.dequeueInputBuffer(0); - du = depacketizer.pollNextDecodeUnit(); - - // Stop if we can't get a DU or input buffer - if (du == null || inputIndex == -1) { - break; - } - - submitDecodeUnit(du, videoDecoderInputBuffers[inputIndex], inputIndex); - - du = null; - inputIndex = -1; - } - } catch (Exception e) { - inputIndex = -1; - handleDecoderException(MediaCodecDecoderRenderer.this, e, null, 0); - } - } - - // Grab an input buffer if we don't have one already. - // This way we can have one ready hopefully by the time - // the depacketizer is done with this frame. It's important - // that this can timeout because it's possible that we could exhaust - // the decoder's input buffers and deadlocks because aren't pulling - // frames out of the other end. - if (inputIndex == -1) { - try { - // If we've got a DU waiting to be given to the decoder, - // wait a full 3 ms for an input buffer. Otherwise - // just see if we can get one immediately. - inputIndex = videoDecoder.dequeueInputBuffer(du != null ? 3000 : 0); - } catch (Exception e) { - inputIndex = -1; - handleDecoderException(MediaCodecDecoderRenderer.this, e, null, 0); - } - } - - // Grab a decode unit if we don't have one already - if (du == null) { - du = depacketizer.pollNextDecodeUnit(); - } - - // If we've got both a decode unit and an input buffer, we'll - // submit now. Otherwise, we wait until we have one. - if (du != null && inputIndex >= 0) { - submitDecodeUnit(du, videoDecoderInputBuffers[inputIndex], inputIndex); - - // DU and input buffer have both been consumed - du = null; - inputIndex = -1; - } - - // Try to output a frame + while (!isInterrupted()) { try { - int outIndex = videoDecoder.dequeueOutputBuffer(info, 0); - + // Try to output a frame + int outIndex = videoDecoder.dequeueOutputBuffer(info, 50000); if (outIndex >= 0) { long presentationTimeUs = info.presentationTimeUs; int lastIndex = outIndex; @@ -264,6 +153,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { // Get the last output buffer in the queue while ((outIndex = videoDecoder.dequeueOutputBuffer(info, 0)) >= 0) { videoDecoder.releaseOutputBuffer(lastIndex, false); + lastIndex = outIndex; presentationTimeUs = info.presentationTimeUs; } @@ -272,33 +162,28 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { videoDecoder.releaseOutputBuffer(lastIndex, true); // Add delta time to the totals (excluding probable outliers) - long delta = System.currentTimeMillis()-(presentationTimeUs/1000); + long delta = System.currentTimeMillis() - (presentationTimeUs / 1000); if (delta >= 0 && delta < 1000) { decoderTimeMs += delta; totalTimeMs += delta; } } else { switch (outIndex) { - case MediaCodec.INFO_TRY_AGAIN_LATER: - // Getting an input buffer may already block - // so don't park if we still need to do that - if (inputIndex >= 0) { - LockSupport.parkNanos(1); - } - break; - case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: - LimeLog.info("Output buffers changed"); - break; - case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: - LimeLog.info("Output format changed"); - LimeLog.info("New output Format: " + videoDecoder.getOutputFormat()); - break; - default: - break; + case MediaCodec.INFO_TRY_AGAIN_LATER: + break; + case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: + LimeLog.info("Output buffers changed"); + break; + case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: + LimeLog.info("Output format changed"); + LimeLog.info("New output Format: " + videoDecoder.getOutputFormat()); + break; + default: + break; } } } catch (Exception e) { - handleDecoderException(MediaCodecDecoderRenderer.this, e, null, 0); + handleDecoderException(e, null, 0); } } } @@ -316,11 +201,9 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { // Start the decoder videoDecoder.start(); - // On devices pre-Lollipop, we'll use a rendering thread - if (!ENABLE_ASYNC_RENDERER || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - videoDecoderInputBuffers = videoDecoder.getInputBuffers(); - startRendererThread(); - } + videoDecoderInputBuffers = videoDecoder.getInputBuffers(); + startRendererThread(); + return true; } @@ -357,7 +240,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { timestampUs, codecFlags); break; } catch (Exception e) { - handleDecoderException(this, e, null, codecFlags); + handleDecoderException(e, null, codecFlags); lastException = e; } } @@ -530,8 +413,14 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { @Override public int getCapabilities() { - return adaptivePlayback ? + int caps = 0; + + caps |= adaptivePlayback ? VideoDecoderRenderer.CAPABILITY_ADAPTIVE_RESOLUTION : 0; + + caps |= VideoDecoderRenderer.CAPABILITY_DIRECT_SUBMIT; + + return caps; } @Override @@ -555,6 +444,24 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer { return decoderName; } + @Override + public void directSubmitDecodeUnit(DecodeUnit du) { + int inputIndex; + + for (;;) { + try { + inputIndex = videoDecoder.dequeueInputBuffer(-1); + break; + } catch (Exception e) { + handleDecoderException(e, null, 0); + } + } + + if (inputIndex >= 0) { + submitDecodeUnit(du, videoDecoderInputBuffers[inputIndex], inputIndex); + } + } + public class RendererException extends RuntimeException { private static final long serialVersionUID = 8985937536997012406L;