Workaround the case where a buggy codec causes findSafeDecoder to fail

This commit is contained in:
Cameron Gutman 2014-09-03 20:48:30 -07:00
parent c02e1ed006
commit ae298fbc51
3 changed files with 68 additions and 34 deletions

View File

@ -198,16 +198,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
controllerHandler = new ControllerHandler(conn); controllerHandler = new ControllerHandler(conn);
decoderRenderer = new ConfigurableDecoderRenderer(); decoderRenderer = new ConfigurableDecoderRenderer();
try {
decoderRenderer.initializeWithFlags(drFlags); 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;
}
SurfaceHolder sh = sv.getHolder(); SurfaceHolder sh = sv.getHolder();
if (stretchToFit || !decoderRenderer.isHardwareAccelerated()) { if (stretchToFit || !decoderRenderer.isHardwareAccelerated()) {

View File

@ -22,10 +22,10 @@ public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
return decoderRenderer.setup(width, height, redrawRate, renderTarget, drFlags); 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 || if ((drFlags & VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING) != 0 ||
((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 && ((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 &&
MediaCodecDecoderRenderer.findSafeDecoder() != null)) { MediaCodecDecoderRenderer.findProbableSafeDecoder() != null)) {
decoderRenderer = new MediaCodecDecoderRenderer(); decoderRenderer = new MediaCodecDecoderRenderer();
} }
else { else {

View File

@ -42,7 +42,9 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
static { static {
blacklistedDecoderPrefixes = new LinkedList<String>(); blacklistedDecoderPrefixes = new LinkedList<String>();
// Nothing here right now :) // Software decoders that don't support H264 high profile
blacklistedDecoderPrefixes.add("omx.google");
blacklistedDecoderPrefixes.add("AVCDecoder");
} }
static { static {
@ -70,7 +72,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
return false; return false;
} }
public static void dumpDecoders() { public static void dumpDecoders() throws Exception {
for (int i = 0; i < MediaCodecList.getCodecCount(); i++) { for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
@ -91,10 +93,50 @@ 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 // We declare this method as explicitly throwing Exception
// since some bad decoders can throw IllegalArgumentExceptions unexpectedly // since some bad decoders can throw IllegalArgumentExceptions unexpectedly
// and we want to be sure all callers are handling this possibility // 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++) { for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(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) { public boolean setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
//dumpDecoders(); //dumpDecoders();
// It's nasty to put all this in a try-catch block, MediaCodecInfo decoder = findProbableSafeDecoder();
// but codecs have been known to throw all sorts of crazy runtime exceptions 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 // due to implementation problems
try { try {
MediaCodecInfo safeDecoder = findSafeDecoder(); videoDecoder = MediaCodec.createByCodecName(decoder.getName());
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;
}
} catch (Exception e) { } catch (Exception e) {
return false; 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); MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0); videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0);
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);