Add decoder crash notification and settings reset on continued crashing

This commit is contained in:
Cameron Gutman 2017-09-09 17:40:07 -07:00
parent 34a11c9262
commit 79a9ea7179
6 changed files with 66 additions and 2 deletions

View File

@ -10,6 +10,7 @@ import com.limelight.binding.input.TouchContext;
import com.limelight.binding.input.driver.UsbDriverService; import com.limelight.binding.input.driver.UsbDriverService;
import com.limelight.binding.input.evdev.EvdevListener; import com.limelight.binding.input.evdev.EvdevListener;
import com.limelight.binding.input.virtual_controller.VirtualController; import com.limelight.binding.input.virtual_controller.VirtualController;
import com.limelight.binding.video.CrashListener;
import com.limelight.binding.video.MediaCodecDecoderRenderer; import com.limelight.binding.video.MediaCodecDecoderRenderer;
import com.limelight.binding.video.MediaCodecHelper; import com.limelight.binding.video.MediaCodecHelper;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
@ -34,6 +35,7 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Point; import android.graphics.Point;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
import android.media.AudioManager; import android.media.AudioManager;
@ -81,6 +83,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private VirtualController virtualController; private VirtualController virtualController;
private PreferenceConfiguration prefConfig; private PreferenceConfiguration prefConfig;
private SharedPreferences tombstonePrefs;
private NvConnection conn; private NvConnection conn;
private SpinnerDialog spinner; private SpinnerDialog spinner;
@ -165,6 +168,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Read the stream preferences // Read the stream preferences
prefConfig = PreferenceConfiguration.readPreferences(this); prefConfig = PreferenceConfiguration.readPreferences(this);
tombstonePrefs = Game.this.getSharedPreferences("DecoderTombstone", 0);
// Listen for events on the game surface // Listen for events on the game surface
streamView = findViewById(R.id.surfaceView); streamView = findViewById(R.id.surfaceView);
@ -214,7 +219,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Initialize the MediaCodec helper before creating the decoder // Initialize the MediaCodec helper before creating the decoder
MediaCodecHelper.initializeWithContext(this); MediaCodecHelper.initializeWithContext(this);
decoderRenderer = new MediaCodecDecoderRenderer(prefConfig.videoFormat, prefConfig.bitrate, prefConfig.batterySaver); decoderRenderer = new MediaCodecDecoderRenderer(prefConfig.videoFormat,
prefConfig.bitrate,
prefConfig.batterySaver,
new CrashListener() {
@Override
public void notifyCrash(Exception e) {
// The MediaCodec instance is going down due to a crash
// let's tell the user something when they open the app again
// We must use commit because the app will crash when we return from this function
tombstonePrefs.edit().putInt("CrashCount", tombstonePrefs.getInt("CrashCount", 0) + 1).commit();
}
});
// Display a message to the user if H.265 was forced on but we still didn't find a decoder // Display a message to the user if H.265 was forced on but we still didn't find a decoder
if (prefConfig.videoFormat == PreferenceConfiguration.FORCE_H265_ON && !decoderRenderer.isHevcSupported()) { if (prefConfig.videoFormat == PreferenceConfiguration.FORCE_H265_ON && !decoderRenderer.isHevcSupported()) {
@ -448,6 +465,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Destroy the capture provider // Destroy the capture provider
inputCaptureProvider.destroy(); inputCaptureProvider.destroy();
// Clear the tombstone count
if (tombstonePrefs.getInt("CrashCount", 0) != 0) {
tombstonePrefs.edit().putInt("CrashCount", 0).apply();
}
} }
@Override @Override

View File

@ -31,6 +31,7 @@ import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
@ -163,6 +164,23 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
PreferenceConfiguration.readPreferences(this).smallIconMode); PreferenceConfiguration.readPreferences(this).smallIconMode);
initializeViews(); initializeViews();
SharedPreferences prefs = getSharedPreferences("DecoderTombstone", 0);
int crashCount = prefs.getInt("CrashCount", 0);
if (crashCount == 3) {
// At 3 consecutive crashes, we'll forcefully reset their settings
PreferenceConfiguration.resetStreamingSettings(this);
Dialog.displayDialog(this,
getResources().getString(R.string.title_decoding_reset),
getResources().getString(R.string.message_decoding_reset),
false);
}
else if (crashCount >= 1) {
Dialog.displayDialog(this,
getResources().getString(R.string.title_decoding_error),
getResources().getString(R.string.message_decoding_error),
false);
}
} }
private void startComputerUpdates() { private void startComputerUpdates() {

View File

@ -0,0 +1,5 @@
package com.limelight.binding.video;
public interface CrashListener {
void notifyCrash(Exception e);
}

View File

@ -12,6 +12,7 @@ import com.limelight.nvstream.av.video.VideoDecoderRenderer;
import com.limelight.nvstream.jni.MoonBridge; import com.limelight.nvstream.jni.MoonBridge;
import com.limelight.preferences.PreferenceConfiguration; import com.limelight.preferences.PreferenceConfiguration;
import android.content.SharedPreferences;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCodecInfo; import android.media.MediaCodecInfo;
import android.media.MediaFormat; import android.media.MediaFormat;
@ -47,6 +48,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
private int videoFormat; private int videoFormat;
private SurfaceHolder renderTarget; private SurfaceHolder renderTarget;
private volatile boolean stopping; private volatile boolean stopping;
private CrashListener crashListener;
private boolean needsBaselineSpsHack; private boolean needsBaselineSpsHack;
private SeqParameterSet savedSps; private SeqParameterSet savedSps;
@ -109,10 +111,11 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
this.renderTarget = renderTarget; this.renderTarget = renderTarget;
} }
public MediaCodecDecoderRenderer(int videoFormat, int bitrate, boolean batterySaver) { public MediaCodecDecoderRenderer(int videoFormat, int bitrate, boolean batterySaver, CrashListener crashListener) {
//dumpDecoders(); //dumpDecoders();
this.bitrate = bitrate; this.bitrate = bitrate;
this.crashListener = crashListener;
// Disable spinner threads in battery saver mode // Disable spinner threads in battery saver mode
if (batterySaver) { if (batterySaver) {
@ -317,6 +320,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
// This isn't the first time we've had an exception processing video // This isn't the first time we've had an exception processing video
if (System.currentTimeMillis() - initialExceptionTimestamp >= EXCEPTION_REPORT_DELAY_MS) { if (System.currentTimeMillis() - initialExceptionTimestamp >= EXCEPTION_REPORT_DELAY_MS) {
// It's been over 3 seconds and we're still getting exceptions. Throw the original now. // It's been over 3 seconds and we're still getting exceptions. Throw the original now.
crashListener.notifyCrash(initialException);
throw initialException; throw initialException;
} }
} }
@ -911,6 +915,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
str += "Format: "+renderer.videoFormat+"\n"; str += "Format: "+renderer.videoFormat+"\n";
str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n"; str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n";
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n"; str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
str += "Build fingerprint: "+Build.FINGERPRINT+"\n";
str += "Initial video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n"; str += "Initial video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
str += "FPS target: "+renderer.refreshRate+"\n"; str += "FPS target: "+renderer.refreshRate+"\n";
str += "Bitrate: "+renderer.bitrate+" Mbps \n"; str += "Bitrate: "+renderer.bitrate+" Mbps \n";

View File

@ -130,6 +130,16 @@ public class PreferenceConfiguration {
} }
} }
public static void resetStreamingSettings(Context context) {
// We consider resolution, FPS, bitrate, and video format as "streaming settings" here
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit()
.remove(BITRATE_PREF_STRING)
.remove(RES_FPS_PREF_STRING)
.remove(VIDEO_FORMAT_PREF_STRING)
.apply();
}
public static PreferenceConfiguration readPreferences(Context context) { public static PreferenceConfiguration readPreferences(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
PreferenceConfiguration config = new PreferenceConfiguration(); PreferenceConfiguration config = new PreferenceConfiguration();

View File

@ -47,6 +47,10 @@
<string name="error_404">GFE returned an HTTP 404 error. Make sure your PC is running a supported GPU. <string name="error_404">GFE returned an HTTP 404 error. Make sure your PC is running a supported GPU.
Using remote desktop software can also cause this error. Try rebooting your machine or reinstalling GFE. Using remote desktop software can also cause this error. Try rebooting your machine or reinstalling GFE.
</string> </string>
<string name="title_decoding_error">Video Decoder Crashed</string>
<string name="message_decoding_error">Moonlight has crashed due to a issue with this device\'s video decoder. Try adjusting the streaming settings if the crashes continue.</string>
<string name="title_decoding_reset">Video Settings Reset</string>
<string name="message_decoding_reset">Your device\'s video decoder continues to crash at your selected streaming settings. Your streaming settings have been reset to default.</string>
<!-- Start application messages --> <!-- Start application messages -->
<string name="conn_establishing_title">Establishing Connection</string> <string name="conn_establishing_title">Establishing Connection</string>