From ae298fbc518963c73300c6c06e0ca95633e719a7 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 3 Sep 2014 20:48:30 -0700 Subject: [PATCH] Workaround the case where a buggy codec causes findSafeDecoder to fail --- src/com/limelight/Game.java | 11 +-- .../video/ConfigurableDecoderRenderer.java | 4 +- .../video/MediaCodecDecoderRenderer.java | 87 ++++++++++++++----- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/src/com/limelight/Game.java b/src/com/limelight/Game.java index 9cd58ea1..9d6fa12c 100644 --- a/src/com/limelight/Game.java +++ b/src/com/limelight/Game.java @@ -198,16 +198,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, controllerHandler = new ControllerHandler(conn); decoderRenderer = new ConfigurableDecoderRenderer(); - - try { - decoderRenderer.initializeWithFlags(drFlags); - } catch (Exception e) { - Dialog.displayDialog(this, "Hardware Decoder Failure", - "The hardware decoder failed to initialize. First, try restarting your device."+ - "If the issue persists, please send an email to the app developer. Forcing software decoding" + - "can circumvent this error if needed.", true); - return; - } + decoderRenderer.initializeWithFlags(drFlags); SurfaceHolder sh = sv.getHolder(); if (stretchToFit || !decoderRenderer.isHardwareAccelerated()) { diff --git a/src/com/limelight/binding/video/ConfigurableDecoderRenderer.java b/src/com/limelight/binding/video/ConfigurableDecoderRenderer.java index 1ee9843b..856192a8 100644 --- a/src/com/limelight/binding/video/ConfigurableDecoderRenderer.java +++ b/src/com/limelight/binding/video/ConfigurableDecoderRenderer.java @@ -22,10 +22,10 @@ public class ConfigurableDecoderRenderer implements VideoDecoderRenderer { return decoderRenderer.setup(width, height, redrawRate, renderTarget, drFlags); } - public void initializeWithFlags(int drFlags) throws Exception { + public void initializeWithFlags(int drFlags) { if ((drFlags & VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING) != 0 || ((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 && - MediaCodecDecoderRenderer.findSafeDecoder() != null)) { + MediaCodecDecoderRenderer.findProbableSafeDecoder() != null)) { decoderRenderer = new MediaCodecDecoderRenderer(); } else { diff --git a/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java index 037d703e..393abae8 100644 --- a/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java +++ b/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -42,7 +42,9 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { static { blacklistedDecoderPrefixes = new LinkedList(); - // Nothing here right now :) + // Software decoders that don't support H264 high profile + blacklistedDecoderPrefixes.add("omx.google"); + blacklistedDecoderPrefixes.add("AVCDecoder"); } static { @@ -70,7 +72,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { return false; } - public static void dumpDecoders() { + public static void dumpDecoders() throws Exception { for (int i = 0; i < MediaCodecList.getCodecCount(); i++) { MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); @@ -90,11 +92,51 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { } } } + + private static MediaCodecInfo findFirstDecoder() { + for (int i = 0; i < MediaCodecList.getCodecCount(); i++) { + MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); + + // Skip encoders + if (codecInfo.isEncoder()) { + continue; + } + + // Check for explicitly blacklisted decoders + if (isDecoderInList(blacklistedDecoderPrefixes, codecInfo.getName())) { + LimeLog.info("Skipping blacklisted decoder: "+codecInfo.getName()); + continue; + } + + // Find a decoder that supports H.264 + for (String mime : codecInfo.getSupportedTypes()) { + if (mime.equalsIgnoreCase("video/avc")) { + LimeLog.info("First decoder choice is "+codecInfo.getName()); + return codecInfo; + } + } + } + + return null; + } + + public static MediaCodecInfo findProbableSafeDecoder() { + // First look for decoders we know are safe + try { + // If this function completes, it will determine if the decoder is safe + return findKnownSafeDecoder(); + } catch (Exception e) { + // Some buggy devices seem to throw exceptions + // from getCapabilitiesForType() so we'll just assume + // they're okay and go with the first one we find + return findFirstDecoder(); + } + } // We declare this method as explicitly throwing Exception // since some bad decoders can throw IllegalArgumentExceptions unexpectedly // and we want to be sure all callers are handling this possibility - public static MediaCodecInfo findSafeDecoder() throws Exception { + private static MediaCodecInfo findKnownSafeDecoder() throws Exception { for (int i = 0; i < MediaCodecList.getCodecCount(); i++) { MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); @@ -135,31 +177,32 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { public boolean setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) { //dumpDecoders(); - // It's nasty to put all this in a try-catch block, - // but codecs have been known to throw all sorts of crazy runtime exceptions + MediaCodecInfo decoder = findProbableSafeDecoder(); + if (decoder == null) { + decoder = findFirstDecoder(); + } + if (decoder == null) { + LimeLog.severe("No available hardware decoder!"); + return false; + } + + // Codecs have been known to throw all sorts of crazy runtime exceptions // due to implementation problems try { - MediaCodecInfo safeDecoder = findSafeDecoder(); - if (safeDecoder != null) { - videoDecoder = MediaCodec.createByCodecName(safeDecoder.getName()); - needsSpsBitstreamFixup = isDecoderInList(spsFixupBitstreamFixupDecoderPrefixes, safeDecoder.getName()); - needsSpsNumRefFixup = isDecoderInList(spsFixupNumRefFixupDecoderPrefixes, safeDecoder.getName()); - if (needsSpsBitstreamFixup) { - LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS bitstream restrictions fixup"); - } - if (needsSpsNumRefFixup) { - LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS ref num fixup"); - } - } - else { - videoDecoder = MediaCodec.createDecoderByType("video/avc"); - needsSpsBitstreamFixup = false; - needsSpsNumRefFixup = false; - } + videoDecoder = MediaCodec.createByCodecName(decoder.getName()); } catch (Exception e) { return false; } + needsSpsBitstreamFixup = isDecoderInList(spsFixupBitstreamFixupDecoderPrefixes, decoder.getName()); + needsSpsNumRefFixup = isDecoderInList(spsFixupNumRefFixupDecoderPrefixes, decoder.getName()); + if (needsSpsBitstreamFixup) { + LimeLog.info("Decoder "+decoder.getName()+" needs SPS bitstream restrictions fixup"); + } + if (needsSpsNumRefFixup) { + LimeLog.info("Decoder "+decoder.getName()+" needs SPS ref num fixup"); + } + MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height); videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0); videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);