mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-19 19:13:03 +00:00
Add decoder crash notification and settings reset on continued crashing
This commit is contained in:
parent
34a11c9262
commit
79a9ea7179
@ -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
|
||||||
|
@ -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() {
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.limelight.binding.video;
|
||||||
|
|
||||||
|
public interface CrashListener {
|
||||||
|
void notifyCrash(Exception e);
|
||||||
|
}
|
@ -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";
|
||||||
|
@ -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();
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user