Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1be5365bb | |||
| c356862ac1 | |||
| fc77322f59 | |||
| 032e944d49 | |||
| 0da47da8d8 | |||
| ebfe843299 | |||
| 827d2362b7 | |||
| 4fa1eb4088 | |||
| 885b59fd52 | |||
| ff5d9f72aa | |||
| 030bb91789 | |||
| 34788b2808 |
+2
-2
@@ -11,8 +11,8 @@ android {
|
||||
minSdk 16
|
||||
targetSdk 34
|
||||
|
||||
versionName "12.0.1"
|
||||
versionCode = 312
|
||||
versionName "12.0.2"
|
||||
versionCode = 313
|
||||
|
||||
// Generate native debug symbols to allow Google Play to symbolicate our native crashes
|
||||
ndk.debugSymbolLevel = 'FULL'
|
||||
|
||||
@@ -127,6 +127,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
private int suppressPipRefCount = 0;
|
||||
private String pcName;
|
||||
private String appName;
|
||||
private NvApp app;
|
||||
private float desiredRefreshRate;
|
||||
|
||||
private InputCaptureProvider inputCaptureProvider;
|
||||
@@ -146,8 +147,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
private int requestedNotificationOverlayVisibility = View.GONE;
|
||||
private TextView performanceOverlayView;
|
||||
|
||||
private ShortcutHelper shortcutHelper;
|
||||
|
||||
private MediaCodecDecoderRenderer decoderRenderer;
|
||||
private boolean reportedCrash;
|
||||
|
||||
@@ -318,10 +317,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
int httpsPort = Game.this.getIntent().getIntExtra(EXTRA_HTTPS_PORT, 0); // 0 is treated as unknown
|
||||
int appId = Game.this.getIntent().getIntExtra(EXTRA_APP_ID, StreamConfiguration.INVALID_APP_ID);
|
||||
String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID);
|
||||
String uuid = Game.this.getIntent().getStringExtra(EXTRA_PC_UUID);
|
||||
boolean appSupportsHdr = Game.this.getIntent().getBooleanExtra(EXTRA_APP_HDR, false);
|
||||
byte[] derCertData = Game.this.getIntent().getByteArrayExtra(EXTRA_SERVER_CERT);
|
||||
|
||||
app = new NvApp(appName != null ? appName : "app", appId, appSupportsHdr);
|
||||
|
||||
X509Certificate serverCert = null;
|
||||
try {
|
||||
if (derCertData != null) {
|
||||
@@ -337,17 +337,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
return;
|
||||
}
|
||||
|
||||
// Report this shortcut being used
|
||||
ComputerDetails computer = new ComputerDetails();
|
||||
computer.name = pcName;
|
||||
computer.uuid = uuid;
|
||||
shortcutHelper = new ShortcutHelper(this);
|
||||
shortcutHelper.reportComputerShortcutUsed(computer);
|
||||
if (appName != null) {
|
||||
// This may be null if launched from the "Resume Session" PC context menu item
|
||||
shortcutHelper.reportGameLaunched(computer, new NvApp(appName, appId, appSupportsHdr));
|
||||
}
|
||||
|
||||
// Initialize the MediaCodec helper before creating the decoder
|
||||
GlPreferences glPrefs = GlPreferences.readPreferences(this);
|
||||
MediaCodecHelper.initialize(this, glPrefs.glRenderer);
|
||||
@@ -479,7 +468,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
.setResolution(prefConfig.width, prefConfig.height)
|
||||
.setLaunchRefreshRate(prefConfig.fps)
|
||||
.setRefreshRate(chosenFrameRate)
|
||||
.setApp(new NvApp(appName != null ? appName : "app", appId, appSupportsHdr))
|
||||
.setApp(app)
|
||||
.setBitrate(prefConfig.bitrate)
|
||||
.setEnableSops(prefConfig.enableSops)
|
||||
.enableLocalAudioPlayback(prefConfig.playHostAudio)
|
||||
@@ -506,7 +495,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
keyboardTranslator = new KeyboardTranslator();
|
||||
|
||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||
inputManager.registerInputDeviceListener(controllerHandler, null);
|
||||
inputManager.registerInputDeviceListener(keyboardTranslator, null);
|
||||
|
||||
// Initialize touch contexts
|
||||
@@ -1090,12 +1078,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||
if (controllerHandler != null) {
|
||||
inputManager.unregisterInputDeviceListener(controllerHandler);
|
||||
controllerHandler.destroy();
|
||||
}
|
||||
if (keyboardTranslator != null) {
|
||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||
inputManager.unregisterInputDeviceListener(keyboardTranslator);
|
||||
}
|
||||
|
||||
@@ -1115,6 +1102,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
inputCaptureProvider.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (isFinishing()) {
|
||||
// Stop any further input device notifications before we lose focus (and pointer capture)
|
||||
if (controllerHandler != null) {
|
||||
controllerHandler.stop();
|
||||
}
|
||||
|
||||
// Ungrab input to prevent further input device notifications
|
||||
setInputGrabState(false);
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
@@ -1869,7 +1871,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
if (prefConfig.absoluteMouseMode) {
|
||||
conn.sendMouseMoveAsMousePosition(deltaX, deltaY, (short)view.getWidth(), (short)view.getHeight());
|
||||
// NB: view may be null, but we can unconditionally use streamView because we don't need to adjust
|
||||
// relative axis deltas for the position of the streamView within the parent's coordinate system.
|
||||
conn.sendMouseMoveAsMousePosition(deltaX, deltaY, (short)streamView.getWidth(), (short)streamView.getHeight());
|
||||
}
|
||||
else {
|
||||
conn.sendMouseMove(deltaX, deltaY);
|
||||
@@ -2319,6 +2323,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Let the display go to sleep now
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
// Stop processing controller input
|
||||
controllerHandler.stop();
|
||||
|
||||
// Ungrab input
|
||||
setInputGrabState(false);
|
||||
|
||||
@@ -2443,6 +2450,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
hideSystemUi(1000);
|
||||
}
|
||||
});
|
||||
|
||||
// Report this shortcut being used (off the main thread to prevent ANRs)
|
||||
ComputerDetails computer = new ComputerDetails();
|
||||
computer.name = pcName;
|
||||
computer.uuid = Game.this.getIntent().getStringExtra(EXTRA_PC_UUID);
|
||||
ShortcutHelper shortcutHelper = new ShortcutHelper(this);
|
||||
shortcutHelper.reportComputerShortcutUsed(computer);
|
||||
if (appName != null) {
|
||||
// This may be null if launched from the "Resume Session" PC context menu item
|
||||
shortcutHelper.reportGameLaunched(computer, app);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -260,6 +260,11 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
updateComputer(details);
|
||||
}
|
||||
});
|
||||
|
||||
// Add a launcher shortcut for this PC (off the main thread to prevent ANRs)
|
||||
if (details.pairState == PairState.PAIRED) {
|
||||
shortcutHelper.createAppViewShortcutForOnlineHost(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -720,11 +725,6 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
}
|
||||
}
|
||||
|
||||
// Add a launcher shortcut for this PC
|
||||
if (details.pairState == PairState.PAIRED) {
|
||||
shortcutHelper.createAppViewShortcutForOnlineHost(details);
|
||||
}
|
||||
|
||||
if (existingEntry != null) {
|
||||
// Replace the information in the existing entry
|
||||
existingEntry.details = details;
|
||||
|
||||
@@ -114,6 +114,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
private final double stickDeadzone;
|
||||
private final InputDeviceContext defaultContext = new InputDeviceContext();
|
||||
private final GameGestures gestures;
|
||||
private final InputManager inputManager;
|
||||
private final Vibrator deviceVibrator;
|
||||
private final VibratorManager deviceVibratorManager;
|
||||
private final SensorManager deviceSensorManager;
|
||||
@@ -134,6 +135,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
this.prefConfig = prefConfig;
|
||||
this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
this.deviceSensorManager = (SensorManager) activityContext.getSystemService(Context.SENSOR_SERVICE);
|
||||
this.inputManager = (InputManager) activityContext.getSystemService(Context.INPUT_SERVICE);
|
||||
this.mainThreadHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
// Create a HandlerThread to process battery state updates. These can be slow enough
|
||||
@@ -205,6 +207,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
// currentControllers set which will allow them to properly unplug
|
||||
// if they are removed.
|
||||
initialControllers = getAttachedControllerMask(activityContext);
|
||||
|
||||
// Register ourselves for input device notifications
|
||||
inputManager.registerInputDeviceListener(this, null);
|
||||
}
|
||||
|
||||
private static InputDevice.MotionRange getMotionRangeForJoystickAxis(InputDevice dev, int axis) {
|
||||
@@ -260,9 +265,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop new device contexts from being created or used
|
||||
stopped = true;
|
||||
|
||||
// Unregister our input device callbacks
|
||||
inputManager.unregisterInputDeviceListener(this);
|
||||
|
||||
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
||||
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
||||
deviceContext.destroy();
|
||||
@@ -293,6 +305,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public void enableSensors() {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
||||
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
||||
deviceContext.enableSensors();
|
||||
@@ -762,6 +778,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this device has a usable RGB LED and cache that result
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
for (Light light : dev.getLightsManager().getLights()) {
|
||||
if (light.hasRgbControl()) {
|
||||
context.hasRgbLed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detect if the gamepad has Mode and Select buttons according to the Android key layouts.
|
||||
// We do this first because other codepaths below may override these defaults.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
@@ -2020,6 +2046,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
boolean foundMatchingDevice = false;
|
||||
boolean vibrated = false;
|
||||
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
||||
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
||||
|
||||
@@ -2080,6 +2110,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public void handleRumbleTriggers(short controllerNumber, short leftTrigger, short rightTrigger) {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
||||
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
||||
@@ -2191,6 +2225,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public void handleSetMotionEventState(final short controllerNumber, final byte motionType, short reportRateHz) {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Report rate is restricted to <= 200 Hz without the HIGH_SAMPLING_RATE_SENSORS permission
|
||||
reportRateHz = (short) Math.min(200, reportRateHz);
|
||||
|
||||
@@ -2252,11 +2290,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public void handleSetControllerLED(short controllerNumber, byte r, byte g, byte b) {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
||||
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
||||
|
||||
if (deviceContext.controllerNumber == controllerNumber) {
|
||||
// Ignore input devices without an RGB LED
|
||||
if (deviceContext.controllerNumber == controllerNumber && deviceContext.hasRgbLed) {
|
||||
// Create a new light session if one doesn't already exist
|
||||
if (deviceContext.lightsSession == null) {
|
||||
deviceContext.lightsSession = deviceContext.inputDevice.getLightsManager().openSession();
|
||||
@@ -2898,6 +2941,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
public InputDevice inputDevice;
|
||||
|
||||
public boolean hasRgbLed;
|
||||
public LightsManager.LightsSession lightsSession;
|
||||
|
||||
// These are BatteryState values, not Moonlight values
|
||||
@@ -3065,19 +3109,20 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
capabilities |= MoonBridge.LI_CCAP_RUMBLE;
|
||||
}
|
||||
|
||||
if (inputDevice.getBatteryState().isPresent()) {
|
||||
// Calling InputDevice.getBatteryState() to see if a battery is present
|
||||
// performs a Binder transaction that can cause ANRs on some devices.
|
||||
// To avoid this, we will just claim we can report battery state for all
|
||||
// external gamepad devices on Android S. If it turns out that no battery
|
||||
// is actually present, we'll just report unknown battery state to the host.
|
||||
if (external) {
|
||||
capabilities |= MoonBridge.LI_CCAP_BATTERY_STATE;
|
||||
}
|
||||
|
||||
for (Light light : inputDevice.getLightsManager().getLights()) {
|
||||
if (light.hasRgbControl()) {
|
||||
// Light.hasRgbControl() was totally broken prior to Android 14.
|
||||
// It always returned true because LIGHT_CAPABILITY_RGB was defined as 0,
|
||||
// so we will just guess RGB is supported if it's a PlayStation controller.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE || type == MoonBridge.LI_CTYPE_PS) {
|
||||
capabilities |= MoonBridge.LI_CCAP_RGB_LED;
|
||||
}
|
||||
}
|
||||
// Light.hasRgbControl() was totally broken prior to Android 14.
|
||||
// It always returned true because LIGHT_CAPABILITY_RGB was defined as 0,
|
||||
// so we will just guess RGB is supported if it's a PlayStation controller.
|
||||
if (hasRgbLed && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE || type == MoonBridge.LI_CTYPE_PS)) {
|
||||
capabilities |= MoonBridge.LI_CCAP_RGB_LED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -65,6 +65,9 @@ public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptu
|
||||
public void showCursor() {
|
||||
super.showCursor();
|
||||
|
||||
// It is important to unregister the listener *before* releasing pointer capture,
|
||||
// because releasing pointer capture can cause an onInputDeviceChanged() callback
|
||||
// for devices with a touchpad (like a DS4 controller).
|
||||
inputManager.unregisterInputDeviceListener(this);
|
||||
targetView.releasePointerCapture();
|
||||
}
|
||||
|
||||
@@ -190,8 +190,13 @@ public class StreamSettings extends Activity {
|
||||
}
|
||||
|
||||
private void addNativeFrameRateEntry(float framerate) {
|
||||
int frameRateRounded = Math.round(framerate);
|
||||
if (frameRateRounded == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.FPS_PREF_STRING);
|
||||
String fpsValue = Integer.toString(Math.round(framerate));
|
||||
String fpsValue = Integer.toString(frameRateRounded);
|
||||
String fpsName = getResources().getString(R.string.resolution_prefix_native) +
|
||||
" (" + fpsValue + " " + getResources().getString(R.string.fps_suffix_fps) + ")";
|
||||
|
||||
@@ -342,12 +347,11 @@ public class StreamSettings extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
int maxSupportedFps = 0;
|
||||
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
||||
float maxSupportedFps = display.getRefreshRate();
|
||||
|
||||
// Hide non-supported resolution/FPS combinations
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
||||
|
||||
int maxSupportedResW = 0;
|
||||
|
||||
// Add a native resolution with any insets included for users that don't want content
|
||||
@@ -415,7 +419,7 @@ public class StreamSettings extends Activity {
|
||||
}
|
||||
|
||||
if (candidate.getRefreshRate() > maxSupportedFps) {
|
||||
maxSupportedFps = (int)candidate.getRefreshRate();
|
||||
maxSupportedFps = candidate.getRefreshRate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,7 +512,7 @@ public class StreamSettings extends Activity {
|
||||
// getRealMetrics() function (unlike the lies that getWidth() and getHeight()
|
||||
// tell to us).
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
getActivity().getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
|
||||
display.getRealMetrics(metrics);
|
||||
int width = Math.max(metrics.widthPixels, metrics.heightPixels);
|
||||
int height = Math.min(metrics.widthPixels, metrics.heightPixels);
|
||||
addNativeResolutionEntries(width, height, false);
|
||||
@@ -516,7 +520,6 @@ public class StreamSettings extends Activity {
|
||||
else {
|
||||
// On Android 4.1, we have to resort to reflection to invoke hidden APIs
|
||||
// to get the real screen dimensions.
|
||||
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
||||
try {
|
||||
Method getRawHeightFunc = Display.class.getMethod("getRawHeight");
|
||||
Method getRawWidthFunc = Display.class.getMethod("getRawWidth");
|
||||
@@ -607,7 +610,6 @@ public class StreamSettings extends Activity {
|
||||
category.removePreference(findPreference("checkbox_enable_hdr"));
|
||||
}
|
||||
else {
|
||||
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
||||
Display.HdrCapabilities hdrCaps = display.getHdrCapabilities();
|
||||
|
||||
// We must now ensure our display is compatible with HDR10
|
||||
|
||||
Submodule app/src/main/jni/moonlight-core/moonlight-common-c updated: c86f49ee7f...5de4a5b85a
@@ -126,8 +126,8 @@
|
||||
<string name="summary_resolution_list">Le réglage de valeurs trop élevées pour votre appareil peut provoquer un retard ou un plantage.</string>
|
||||
<string name="title_fps_list">Fréquence d\'images vidéo</string>
|
||||
<string name="summary_fps_list">Augmenter pour un flux vidéo plus lisse. Diminution pour de meilleures performances sur les périphériques bas de gamme.</string>
|
||||
<string name="title_seekbar_bitrate">Sélectionnez le bitrate vidéo à obtenir</string>
|
||||
<string name="summary_seekbar_bitrate">Bitrate inférieur pour réduire la saccade. Augmentez le bitrate pour augmenter la qualité de l\'image.</string>
|
||||
<string name="title_seekbar_bitrate">Bitrate vidéo</string>
|
||||
<string name="summary_seekbar_bitrate">Augmentez-le pour une meilleure qualité d’image. Diminuez-le pour améliorer les performances sur les connexions plus lentes.</string>
|
||||
<string name="title_checkbox_stretch_video">Étirez la vidéo en plein écran</string>
|
||||
<string name="title_checkbox_disable_warnings">Désactiver les messages d\'avertissement</string>
|
||||
<string name="title_checkbox_enable_pip">Activer le mode observateur dans l\'image</string>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<string name="unpairing">Disaccoppiamento…</string>
|
||||
<string name="unpair_success">Disaccoppiato con successo</string>
|
||||
<string name="unpair_fail">Disaccoppiamento fallito</string>
|
||||
<string name="unpair_error">PC non accoppiato</string>
|
||||
<string name="unpair_error">Il dispositivo non è stato accoppiato</string>
|
||||
<!-- Errors -->
|
||||
<string name="error_pc_offline">PC offline</string>
|
||||
<string name="error_manager_not_running">Il servizio ComputerManager non è avviato. Attendi qualche secondo o riavvia l\'applicazione.</string>
|
||||
@@ -91,7 +91,7 @@
|
||||
<string name="addpc_wrong_sitelocal">Quell\'indirizzo non sembra corretto. È necessario utilizzare l\'indirizzo IP pubblico del router per lo streaming su Internet.</string>
|
||||
<!-- Preferences -->
|
||||
<string name="category_basic_settings">Impostazioni generali</string>
|
||||
<string name="title_resolution_list">Risoluzione e FPS</string>
|
||||
<string name="title_resolution_list">Risoluzione video</string>
|
||||
<string name="summary_resolution_list">Aumentare per migliorare la nitidezza dell\'immagine. Diminuire per aumentare le prestazioni su dispositivi di fascia più bassa e reti più lente.</string>
|
||||
<string name="title_seekbar_bitrate">Velocità di trasmissione video</string>
|
||||
<string name="summary_seekbar_bitrate">Aumentare per migliorare la qualità dell\'immagine. Diminuire per aumentare le prestazioni su reti più lente.</string>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
- Fixed connection issue with some hosts running GeForce Experience and older Sunshine versions
|
||||
- Fixed native frame rate streaming support on older versions of Android
|
||||
- Stability improvements
|
||||
Reference in New Issue
Block a user