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

Uc 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

+=EvTctnE|m!{i=MM_Q>WegsY&S_O$h1-k*N7 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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;