Compare commits

..
24 Commits
Author SHA1 Message Date
Cameron Gutman f1be5365bb Version 12.0.2 2023-11-01 20:35:57 -05:00
Cameron Gutman c356862ac1 Avoid unnecessary reinitialization of PS4/PS5 gamepads during stream exit
onInputDeviceChanged() is triggered by starting/stopping pointer capture, so we should
unregister our callbacks before that happens to avoid triggering several gamepad context
reinitializations right as the stream is exiting
2023-10-29 16:45:07 -05:00
Cameron Gutman fc77322f59 Merge remote-tracking branch 'origin/weblate' 2023-10-29 15:51:59 -05:00
Cameron Gutman 032e944d49 Update moonlight-common-c with multi-homed host fix 2023-10-29 15:34:09 -05:00
Cameron Gutman 0da47da8d8 Move shortcut creation/updates off the main thread for the common cases
These caused quite a few ANRs due to long Binder calls in ShortcutManager.getDynamicShortcuts()
2023-10-26 00:24:44 -05:00
Cameron Gutman ebfe843299 Don't process incoming input device requests from the host after we've stopped 2023-10-26 00:05:24 -05:00
Cameron Gutman 827d2362b7 Don't create LightsSessions for devices without an RGB LED 2023-10-25 23:53:35 -05:00
Cameron Gutman 4fa1eb4088 Fix max FPS detection on Lollipop and earlier 2023-10-25 23:47:40 -05:00
Cameron Gutman 885b59fd52 Fix NPE when receiving non-view-associated mouse events with absolute mouse mode enabled 2023-10-25 23:13:54 -05:00
Cameron Gutman ff5d9f72aa Fix ANRs trying to get battery state during controller arrival 2023-10-25 23:13:11 -05:00
Marocco2 030bb91789 Translated using Weblate (Italian)
Currently translated at 100.0% (242 of 242 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/it/
2023-10-22 09:01:23 +00:00
Jorys Paulin 34788b2808 Translated using Weblate (French)
Currently translated at 100.0% (242 of 242 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/fr/
2023-10-19 06:09:40 +02:00
Cameron Gutman abc4123c52 Version 12.0.1 2023-10-17 22:39:49 -05:00
Cameron Gutman 26f8c0842e Merge remote-tracking branch 'origin/weblate' 2023-10-16 23:57:21 -05:00
Cameron Gutman d430d83ba8 Add clickpad button emulation combo (Select+L1) 2023-10-16 23:56:56 -05:00
Cameron Gutman a52f189fb1 Update ShieldControllerExtensions to fix crash in getBatteryPercentage() 2023-10-16 23:55:39 -05:00
Cameron Gutman dc1045b69e Don't lie to our own clickpad heuristics when overriding the controller type 2023-10-16 23:38:12 -05:00
Cameron Gutman 3a89dbf4ab Update moonlight-common-c 2023-10-16 23:17:08 -05:00
Cameron Gutman d69b4eca1e Disable gamepad motion sensors by default on Android 12 due to an OS bug 2023-10-16 23:16:55 -05:00
Marocco2 568bba82f0 Translated using Weblate (Italian)
Currently translated at 100.0% (242 of 242 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/it/
2023-10-16 04:19:16 +00:00
Smoukus b52e6c88ec Translated using Weblate (German)
Currently translated at 92.5% (224 of 242 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/de/
2023-10-16 04:19:14 +00:00
Cameron Gutman 720595091e Change AV1 bitrate multiplier to match HEVC 2023-10-15 12:49:42 -05:00
Cameron Gutman fe929c8e58 Fix debug build assert 2023-10-13 23:23:42 -05:00
Cameron Gutman 9ecec1eb3c Add support for H.264/HEVC bitstreams with multiple sets of parameter set NALUs 2023-10-13 23:21:50 -05:00
14 changed files with 456 additions and 297 deletions

No files matched your search

+3 -3
View File
@@ -11,8 +11,8 @@ android {
minSdk 16 minSdk 16
targetSdk 34 targetSdk 34
versionName "12.0" versionName "12.0.2"
versionCode = 311 versionCode = 313
// Generate native debug symbols to allow Google Play to symbolicate our native crashes // Generate native debug symbols to allow Google Play to symbolicate our native crashes
ndk.debugSymbolLevel = 'FULL' ndk.debugSymbolLevel = 'FULL'
@@ -143,5 +143,5 @@ dependencies {
implementation 'com.squareup.okio:okio:1.17.5' implementation 'com.squareup.okio:okio:1.17.5'
// 3.5.8 requires minSdk 19, uses StandardCharsets.UTF_8 internally // 3.5.8 requires minSdk 19, uses StandardCharsets.UTF_8 internally
implementation 'org.jmdns:jmdns:3.5.7' implementation 'org.jmdns:jmdns:3.5.7'
implementation 'com.github.cgutman:ShieldControllerExtensions:1.0' implementation 'com.github.cgutman:ShieldControllerExtensions:1.0.1'
} }
+38 -20
View File
@@ -127,6 +127,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private int suppressPipRefCount = 0; private int suppressPipRefCount = 0;
private String pcName; private String pcName;
private String appName; private String appName;
private NvApp app;
private float desiredRefreshRate; private float desiredRefreshRate;
private InputCaptureProvider inputCaptureProvider; private InputCaptureProvider inputCaptureProvider;
@@ -146,8 +147,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private int requestedNotificationOverlayVisibility = View.GONE; private int requestedNotificationOverlayVisibility = View.GONE;
private TextView performanceOverlayView; private TextView performanceOverlayView;
private ShortcutHelper shortcutHelper;
private MediaCodecDecoderRenderer decoderRenderer; private MediaCodecDecoderRenderer decoderRenderer;
private boolean reportedCrash; 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 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); int appId = Game.this.getIntent().getIntExtra(EXTRA_APP_ID, StreamConfiguration.INVALID_APP_ID);
String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID); 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); boolean appSupportsHdr = Game.this.getIntent().getBooleanExtra(EXTRA_APP_HDR, false);
byte[] derCertData = Game.this.getIntent().getByteArrayExtra(EXTRA_SERVER_CERT); byte[] derCertData = Game.this.getIntent().getByteArrayExtra(EXTRA_SERVER_CERT);
app = new NvApp(appName != null ? appName : "app", appId, appSupportsHdr);
X509Certificate serverCert = null; X509Certificate serverCert = null;
try { try {
if (derCertData != null) { if (derCertData != null) {
@@ -337,17 +337,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return; 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 // Initialize the MediaCodec helper before creating the decoder
GlPreferences glPrefs = GlPreferences.readPreferences(this); GlPreferences glPrefs = GlPreferences.readPreferences(this);
MediaCodecHelper.initialize(this, glPrefs.glRenderer); MediaCodecHelper.initialize(this, glPrefs.glRenderer);
@@ -479,14 +468,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
.setResolution(prefConfig.width, prefConfig.height) .setResolution(prefConfig.width, prefConfig.height)
.setLaunchRefreshRate(prefConfig.fps) .setLaunchRefreshRate(prefConfig.fps)
.setRefreshRate(chosenFrameRate) .setRefreshRate(chosenFrameRate)
.setApp(new NvApp(appName != null ? appName : "app", appId, appSupportsHdr)) .setApp(app)
.setBitrate(prefConfig.bitrate) .setBitrate(prefConfig.bitrate)
.setEnableSops(prefConfig.enableSops) .setEnableSops(prefConfig.enableSops)
.enableLocalAudioPlayback(prefConfig.playHostAudio) .enableLocalAudioPlayback(prefConfig.playHostAudio)
.setMaxPacketSize(1392) .setMaxPacketSize(1392)
.setRemoteConfiguration(StreamConfiguration.STREAM_CFG_AUTO) // NvConnection will perform LAN and VPN detection .setRemoteConfiguration(StreamConfiguration.STREAM_CFG_AUTO) // NvConnection will perform LAN and VPN detection
.setHevcBitratePercentageMultiplier(75) .setHevcBitratePercentageMultiplier(75)
.setAv1BitratePercentageMultiplier(60) .setAv1BitratePercentageMultiplier(75)
.setSupportedVideoFormats(supportedVideoFormats) .setSupportedVideoFormats(supportedVideoFormats)
.setAttachedGamepadMask(gamepadMask) .setAttachedGamepadMask(gamepadMask)
.setClientRefreshRateX100((int)(displayRefreshRate * 100)) .setClientRefreshRateX100((int)(displayRefreshRate * 100))
@@ -506,7 +495,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
keyboardTranslator = new KeyboardTranslator(); keyboardTranslator = new KeyboardTranslator();
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE); InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
inputManager.registerInputDeviceListener(controllerHandler, null);
inputManager.registerInputDeviceListener(keyboardTranslator, null); inputManager.registerInputDeviceListener(keyboardTranslator, null);
// Initialize touch contexts // Initialize touch contexts
@@ -1090,12 +1078,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
if (controllerHandler != null) { if (controllerHandler != null) {
inputManager.unregisterInputDeviceListener(controllerHandler);
controllerHandler.destroy(); controllerHandler.destroy();
} }
if (keyboardTranslator != null) { if (keyboardTranslator != null) {
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
inputManager.unregisterInputDeviceListener(keyboardTranslator); inputManager.unregisterInputDeviceListener(keyboardTranslator);
} }
@@ -1115,6 +1102,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
inputCaptureProvider.destroy(); 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 @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
@@ -1869,7 +1871,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (deltaX != 0 || deltaY != 0) { if (deltaX != 0 || deltaY != 0) {
if (prefConfig.absoluteMouseMode) { 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 { else {
conn.sendMouseMove(deltaX, deltaY); conn.sendMouseMove(deltaX, deltaY);
@@ -2319,6 +2323,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Let the display go to sleep now // Let the display go to sleep now
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Stop processing controller input
controllerHandler.stop();
// Ungrab input // Ungrab input
setInputGrabState(false); setInputGrabState(false);
@@ -2443,6 +2450,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
hideSystemUi(1000); 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 @Override
+5 -5
View File
@@ -260,6 +260,11 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
updateComputer(details); 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) { if (existingEntry != null) {
// Replace the information in the existing entry // Replace the information in the existing entry
existingEntry.details = details; existingEntry.details = details;
@@ -63,6 +63,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private static final int EMULATING_SPECIAL = 0x1; private static final int EMULATING_SPECIAL = 0x1;
private static final int EMULATING_SELECT = 0x2; private static final int EMULATING_SELECT = 0x2;
private static final int EMULATING_TOUCHPAD = 0x4;
private static final short MAX_GAMEPADS = 16; // Limited by bits in activeGamepadMask private static final short MAX_GAMEPADS = 16; // Limited by bits in activeGamepadMask
@@ -113,6 +114,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private final double stickDeadzone; private final double stickDeadzone;
private final InputDeviceContext defaultContext = new InputDeviceContext(); private final InputDeviceContext defaultContext = new InputDeviceContext();
private final GameGestures gestures; private final GameGestures gestures;
private final InputManager inputManager;
private final Vibrator deviceVibrator; private final Vibrator deviceVibrator;
private final VibratorManager deviceVibratorManager; private final VibratorManager deviceVibratorManager;
private final SensorManager deviceSensorManager; private final SensorManager deviceSensorManager;
@@ -133,6 +135,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
this.prefConfig = prefConfig; this.prefConfig = prefConfig;
this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE); this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE);
this.deviceSensorManager = (SensorManager) activityContext.getSystemService(Context.SENSOR_SERVICE); this.deviceSensorManager = (SensorManager) activityContext.getSystemService(Context.SENSOR_SERVICE);
this.inputManager = (InputManager) activityContext.getSystemService(Context.INPUT_SERVICE);
this.mainThreadHandler = new Handler(Looper.getMainLooper()); this.mainThreadHandler = new Handler(Looper.getMainLooper());
// Create a HandlerThread to process battery state updates. These can be slow enough // Create a HandlerThread to process battery state updates. These can be slow enough
@@ -204,6 +207,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
// currentControllers set which will allow them to properly unplug // currentControllers set which will allow them to properly unplug
// if they are removed. // if they are removed.
initialControllers = getAttachedControllerMask(activityContext); initialControllers = getAttachedControllerMask(activityContext);
// Register ourselves for input device notifications
inputManager.registerInputDeviceListener(this, null);
} }
private static InputDevice.MotionRange getMotionRangeForJoystickAxis(InputDevice dev, int axis) { private static InputDevice.MotionRange getMotionRangeForJoystickAxis(InputDevice dev, int axis) {
@@ -259,9 +265,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
public void stop() { public void stop() {
if (stopped) {
return;
}
// Stop new device contexts from being created or used // Stop new device contexts from being created or used
stopped = true; stopped = true;
// Unregister our input device callbacks
inputManager.unregisterInputDeviceListener(this);
for (int i = 0; i < inputDeviceContexts.size(); i++) { for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i); InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
deviceContext.destroy(); deviceContext.destroy();
@@ -292,6 +305,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
public void enableSensors() { public void enableSensors() {
if (stopped) {
return;
}
for (int i = 0; i < inputDeviceContexts.size(); i++) { for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i); InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
deviceContext.enableSensors(); deviceContext.enableSensors();
@@ -761,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. // 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. // We do this first because other codepaths below may override these defaults.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@@ -2019,6 +2046,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
boolean foundMatchingDevice = false; boolean foundMatchingDevice = false;
boolean vibrated = false; boolean vibrated = false;
if (stopped) {
return;
}
for (int i = 0; i < inputDeviceContexts.size(); i++) { for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i); InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
@@ -2079,6 +2110,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
public void handleRumbleTriggers(short controllerNumber, short leftTrigger, short rightTrigger) { public void handleRumbleTriggers(short controllerNumber, short leftTrigger, short rightTrigger) {
if (stopped) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
for (int i = 0; i < inputDeviceContexts.size(); i++) { for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i); InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
@@ -2190,6 +2225,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
public void handleSetMotionEventState(final short controllerNumber, final byte motionType, short reportRateHz) { 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 // Report rate is restricted to <= 200 Hz without the HIGH_SAMPLING_RATE_SENSORS permission
reportRateHz = (short) Math.min(200, reportRateHz); reportRateHz = (short) Math.min(200, reportRateHz);
@@ -2251,11 +2290,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
public void handleSetControllerLED(short controllerNumber, byte r, byte g, byte b) { public void handleSetControllerLED(short controllerNumber, byte r, byte g, byte b) {
if (stopped) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
for (int i = 0; i < inputDeviceContexts.size(); i++) { for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(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 // Create a new light session if one doesn't already exist
if (deviceContext.lightsSession == null) { if (deviceContext.lightsSession == null) {
deviceContext.lightsSession = deviceContext.inputDevice.getLightsManager().openSession(); deviceContext.lightsSession = deviceContext.inputDevice.getLightsManager().openSession();
@@ -2494,6 +2538,19 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
} }
// Check if we're emulating the touchpad button
if ((context.emulatingButtonFlags & ControllerHandler.EMULATING_TOUCHPAD) != 0)
{
// If either select or LB is up, touchpad comes up too
if ((context.inputMap & ControllerPacket.BACK_FLAG) == 0 ||
(context.inputMap & ControllerPacket.LB_FLAG) == 0)
{
context.inputMap &= ~ControllerPacket.TOUCHPAD_FLAG;
context.emulatingButtonFlags &= ~ControllerHandler.EMULATING_TOUCHPAD;
}
}
sendControllerInputPacket(context); sendControllerInputPacket(context);
if (context.pendingExit && context.inputMap == 0) { if (context.pendingExit && context.inputMap == 0) {
@@ -2685,6 +2742,18 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SELECT; context.emulatingButtonFlags |= ControllerHandler.EMULATING_SELECT;
} }
} }
else if (context.needsClickpadEmulation) {
// Select+LB acts like the clickpad when we're faking a PS4 controller for motion support
if (context.inputMap == (ControllerPacket.BACK_FLAG | ControllerPacket.LB_FLAG) ||
(context.inputMap == ControllerPacket.BACK_FLAG &&
event.getEventTime() - context.lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
{
context.inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.LB_FLAG);
context.inputMap |= ControllerPacket.TOUCHPAD_FLAG;
context.emulatingButtonFlags |= ControllerHandler.EMULATING_TOUCHPAD;
}
}
// If there is a physical select button, we'll use Start+Select as the special button combo // If there is a physical select button, we'll use Start+Select as the special button combo
// otherwise we'll use Start+RB. // otherwise we'll use Start+RB.
@@ -2872,6 +2941,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public InputDevice inputDevice; public InputDevice inputDevice;
public boolean hasRgbLed;
public LightsManager.LightsSession lightsSession; public LightsManager.LightsSession lightsSession;
// These are BatteryState values, not Moonlight values // These are BatteryState values, not Moonlight values
@@ -2914,6 +2984,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public boolean hasMode; public boolean hasMode;
public boolean hasPaddles; public boolean hasPaddles;
public boolean hasShare; public boolean hasShare;
public boolean needsClickpadEmulation;
// Used for OUYA bumper state tracking since they force all buttons // Used for OUYA bumper state tracking since they force all buttons
// up when the OUYA button goes down. We watch the last time we get // up when the OUYA button goes down. We watch the last time we get
@@ -2984,7 +3055,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return; return;
} }
byte type = MoonBridge.LI_CTYPE_UNKNOWN; byte type;
switch (inputDevice.getVendorId()) { switch (inputDevice.getVendorId()) {
case 0x045e: // Microsoft case 0x045e: // Microsoft
type = MoonBridge.LI_CTYPE_XBOX; type = MoonBridge.LI_CTYPE_XBOX;
@@ -3038,19 +3109,20 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
capabilities |= MoonBridge.LI_CCAP_RUMBLE; 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; capabilities |= MoonBridge.LI_CCAP_BATTERY_STATE;
} }
for (Light light : inputDevice.getLightsManager().getLights()) { // Light.hasRgbControl() was totally broken prior to Android 14.
if (light.hasRgbControl()) { // It always returned true because LIGHT_CAPABILITY_RGB was defined as 0,
// Light.hasRgbControl() was totally broken prior to Android 14. // so we will just guess RGB is supported if it's a PlayStation controller.
// It always returned true because LIGHT_CAPABILITY_RGB was defined as 0, if (hasRgbLed && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE || type == MoonBridge.LI_CTYPE_PS)) {
// so we will just guess RGB is supported if it's a PlayStation controller. capabilities |= MoonBridge.LI_CCAP_RGB_LED;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE || type == MoonBridge.LI_CTYPE_PS) {
capabilities |= MoonBridge.LI_CCAP_RGB_LED;
}
}
} }
} }
@@ -3067,10 +3139,18 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
capabilities |= MoonBridge.LI_CCAP_GYRO; capabilities |= MoonBridge.LI_CCAP_GYRO;
} }
// Override the detected controller type if we're emulating motion sensors on an Xbox controller byte reportedType;
if (type != MoonBridge.LI_CTYPE_PS && sensorManager != null) { if (type != MoonBridge.LI_CTYPE_PS && sensorManager != null) {
// Override the detected controller type if we're emulating motion sensors on an Xbox controller
Toast.makeText(activityContext, activityContext.getResources().getText(R.string.toast_controller_type_changed), Toast.LENGTH_LONG).show(); Toast.makeText(activityContext, activityContext.getResources().getText(R.string.toast_controller_type_changed), Toast.LENGTH_LONG).show();
type = MoonBridge.LI_CTYPE_UNKNOWN; reportedType = MoonBridge.LI_CTYPE_UNKNOWN;
// Remember that we should enable the clickpad emulation combo (Select+LB) for this device
needsClickpadEmulation = true;
}
else {
// Report the true type to the host PC if we're not emulating motion sensors
reportedType = type;
} }
// We can perform basic rumble with any vibrator // We can perform basic rumble with any vibrator
@@ -3093,7 +3173,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
conn.sendControllerArrivalEvent((byte)controllerNumber, getActiveControllerMask(), conn.sendControllerArrivalEvent((byte)controllerNumber, getActiveControllerMask(),
type, supportedButtonFlags, capabilities); reportedType, supportedButtonFlags, capabilities);
// After reporting arrival to the host, send initial battery state and begin monitoring // After reporting arrival to the host, send initial battery state and begin monitoring
backgroundThreadHandler.post(batteryStateUpdateRunnable); backgroundThreadHandler.post(batteryStateUpdateRunnable);
@@ -3123,6 +3203,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
this.sensorManager = deviceSensorManager; this.sensorManager = deviceSensorManager;
} }
// Copy state initialized in reportControllerArrival()
this.needsClickpadEmulation = oldContext.needsClickpadEmulation;
// Re-enable sensors on the new context // Re-enable sensors on the new context
enableSensors(); enableSensors();
@@ -65,6 +65,9 @@ public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptu
public void showCursor() { public void showCursor() {
super.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); inputManager.unregisterInputDeviceListener(this);
targetView.releasePointerCapture(); targetView.releasePointerCapture();
} }
@@ -3,6 +3,7 @@ package com.limelight.binding.video;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@@ -48,11 +49,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
private MediaCodecInfo hevcDecoder; private MediaCodecInfo hevcDecoder;
private MediaCodecInfo av1Decoder; private MediaCodecInfo av1Decoder;
private byte[] vpsBuffer; private final ArrayList<byte[]> vpsBuffers = new ArrayList<>();
private byte[] spsBuffer; private final ArrayList<byte[]> spsBuffers = new ArrayList<>();
private byte[] ppsBuffer; private final ArrayList<byte[]> ppsBuffers = new ArrayList<>();
private boolean submittedCsd; private boolean submittedCsd;
private boolean submitCsdNextCall;
private byte[] currentHdrMetadata; private byte[] currentHdrMetadata;
private int nextInputBufferIndex = -1; private int nextInputBufferIndex = -1;
@@ -544,10 +544,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
// After reconfiguration, we must resubmit CSD buffers // After reconfiguration, we must resubmit CSD buffers
submittedCsd = false; submittedCsd = false;
submitCsdNextCall = false; vpsBuffers.clear();
vpsBuffer = null; spsBuffers.clear();
spsBuffer = null; ppsBuffers.clear();
ppsBuffer = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// This will contain the actual accepted input format attributes // This will contain the actual accepted input format attributes
@@ -1414,6 +1413,13 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
activeWindowVideoStats.frameLossEvents++; activeWindowVideoStats.frameLossEvents++;
} }
// Reset CSD data for each IDR frame
if (lastFrameNumber != frameNumber && frameType == MoonBridge.FRAME_TYPE_IDR) {
vpsBuffers.clear();
spsBuffers.clear();
ppsBuffers.clear();
}
lastFrameNumber = frameNumber; lastFrameNumber = frameNumber;
// Flip stats windows roughly every second // Flip stats windows roughly every second
@@ -1462,250 +1468,270 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
activeWindowVideoStats.measurementStartTimestamp = SystemClock.uptimeMillis(); activeWindowVideoStats.measurementStartTimestamp = SystemClock.uptimeMillis();
} }
long timestampUs; boolean csdSubmittedForThisFrame = false;
int codecFlags = 0;
// H264 SPS // IDR frames require special handling for CSD buffer submission
if (decodeUnitType == MoonBridge.BUFFER_TYPE_SPS && (videoFormat & MoonBridge.VIDEO_FORMAT_MASK_H264) != 0) { if (frameType == MoonBridge.FRAME_TYPE_IDR) {
numSpsIn++; // H264 SPS
if (decodeUnitType == MoonBridge.BUFFER_TYPE_SPS && (videoFormat & MoonBridge.VIDEO_FORMAT_MASK_H264) != 0) {
numSpsIn++;
ByteBuffer spsBuf = ByteBuffer.wrap(decodeUnitData); ByteBuffer spsBuf = ByteBuffer.wrap(decodeUnitData);
int startSeqLen = decodeUnitData[2] == 0x01 ? 3 : 4; int startSeqLen = decodeUnitData[2] == 0x01 ? 3 : 4;
// Skip to the start of the NALU data // Skip to the start of the NALU data
spsBuf.position(startSeqLen + 1); spsBuf.position(startSeqLen + 1);
// The H264Utils.readSPS function safely handles // The H264Utils.readSPS function safely handles
// Annex B NALUs (including NALUs with escape sequences) // Annex B NALUs (including NALUs with escape sequences)
SeqParameterSet sps = H264Utils.readSPS(spsBuf); SeqParameterSet sps = H264Utils.readSPS(spsBuf);
// Some decoders rely on H264 level to decide how many buffers are needed // Some decoders rely on H264 level to decide how many buffers are needed
// Since we only need one frame buffered, we'll set the level as low as we can // Since we only need one frame buffered, we'll set the level as low as we can
// for known resolution combinations. Reference frame invalidation may need // for known resolution combinations. Reference frame invalidation may need
// these, so leave them be for those decoders. // these, so leave them be for those decoders.
if (!refFrameInvalidationActive) { if (!refFrameInvalidationActive) {
if (initialWidth <= 720 && initialHeight <= 480 && refreshRate <= 60) { if (initialWidth <= 720 && initialHeight <= 480 && refreshRate <= 60) {
// Max 5 buffered frames at 720x480x60 // Max 5 buffered frames at 720x480x60
LimeLog.info("Patching level_idc to 31"); LimeLog.info("Patching level_idc to 31");
sps.levelIdc = 31; sps.levelIdc = 31;
} }
else if (initialWidth <= 1280 && initialHeight <= 720 && refreshRate <= 60) { else if (initialWidth <= 1280 && initialHeight <= 720 && refreshRate <= 60) {
// Max 5 buffered frames at 1280x720x60 // Max 5 buffered frames at 1280x720x60
LimeLog.info("Patching level_idc to 32"); LimeLog.info("Patching level_idc to 32");
sps.levelIdc = 32; sps.levelIdc = 32;
} }
else if (initialWidth <= 1920 && initialHeight <= 1080 && refreshRate <= 60) { else if (initialWidth <= 1920 && initialHeight <= 1080 && refreshRate <= 60) {
// Max 4 buffered frames at 1920x1080x64 // Max 4 buffered frames at 1920x1080x64
LimeLog.info("Patching level_idc to 42"); LimeLog.info("Patching level_idc to 42");
sps.levelIdc = 42; sps.levelIdc = 42;
} }
else { else {
// Leave the profile alone (currently 5.0) // Leave the profile alone (currently 5.0)
} }
}
// TI OMAP4 requires a reference frame count of 1 to decode successfully. Exynos 4
// also requires this fixup.
//
// I'm doing this fixup for all devices because I haven't seen any devices that
// this causes issues for. At worst, it seems to do nothing and at best it fixes
// issues with video lag, hangs, and crashes.
//
// It does break reference frame invalidation, so we will not do that for decoders
// where we've enabled reference frame invalidation.
if (!refFrameInvalidationActive) {
LimeLog.info("Patching num_ref_frames in SPS");
sps.numRefFrames = 1;
}
// GFE 2.5.11 changed the SPS to add additional extensions. Some devices don't like these
// so we remove them here on old devices unless these devices also support HEVC.
// See getPreferredColorSpace() for further information.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O &&
sps.vuiParams != null &&
hevcDecoder == null &&
av1Decoder == null) {
sps.vuiParams.videoSignalTypePresentFlag = false;
sps.vuiParams.colourDescriptionPresentFlag = false;
sps.vuiParams.chromaLocInfoPresentFlag = false;
}
// Some older devices used to choke on a bitstream restrictions, so we won't provide them
// unless explicitly whitelisted. For newer devices, leave the bitstream restrictions present.
if (needsSpsBitstreamFixup || isExynos4 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
// or max_dec_frame_buffering which increases decoding latency on Tegra.
// If the encoder didn't include VUI parameters in the SPS, add them now
if (sps.vuiParams == null) {
LimeLog.info("Adding VUI parameters");
sps.vuiParams = new VUIParameters();
} }
// GFE 2.5.11 started sending bitstream restrictions // TI OMAP4 requires a reference frame count of 1 to decode successfully. Exynos 4
if (sps.vuiParams.bitstreamRestriction == null) { // also requires this fixup.
LimeLog.info("Adding bitstream restrictions"); //
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction(); // I'm doing this fixup for all devices because I haven't seen any devices that
sps.vuiParams.bitstreamRestriction.motionVectorsOverPicBoundariesFlag = true; // this causes issues for. At worst, it seems to do nothing and at best it fixes
sps.vuiParams.bitstreamRestriction.maxBytesPerPicDenom = 2; // issues with video lag, hangs, and crashes.
sps.vuiParams.bitstreamRestriction.maxBitsPerMbDenom = 1; //
sps.vuiParams.bitstreamRestriction.log2MaxMvLengthHorizontal = 16; // It does break reference frame invalidation, so we will not do that for decoders
sps.vuiParams.bitstreamRestriction.log2MaxMvLengthVertical = 16; // where we've enabled reference frame invalidation.
sps.vuiParams.bitstreamRestriction.numReorderFrames = 0; if (!refFrameInvalidationActive) {
} LimeLog.info("Patching num_ref_frames in SPS");
else { sps.numRefFrames = 1;
LimeLog.info("Patching bitstream restrictions");
} }
// Some devices throw errors if maxDecFrameBuffering < numRefFrames // GFE 2.5.11 changed the SPS to add additional extensions. Some devices don't like these
sps.vuiParams.bitstreamRestriction.maxDecFrameBuffering = sps.numRefFrames; // so we remove them here on old devices unless these devices also support HEVC.
// See getPreferredColorSpace() for further information.
// These values are the defaults for the fields, but they are more aggressive if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O &&
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems. sps.vuiParams != null &&
// We'll leave these alone for "modern" devices just in case they care. hevcDecoder == null &&
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { av1Decoder == null) {
sps.vuiParams.bitstreamRestriction.maxBytesPerPicDenom = 2; sps.vuiParams.videoSignalTypePresentFlag = false;
sps.vuiParams.bitstreamRestriction.maxBitsPerMbDenom = 1; sps.vuiParams.colourDescriptionPresentFlag = false;
sps.vuiParams.chromaLocInfoPresentFlag = false;
} }
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more // Some older devices used to choke on a bitstream restrictions, so we won't provide them
// conservative values by GFE 2.5.11. We'll let those values stand. // unless explicitly whitelisted. For newer devices, leave the bitstream restrictions present.
} if (needsSpsBitstreamFixup || isExynos4 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
else if (sps.vuiParams != null) { // The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
// Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11 // or max_dec_frame_buffering which increases decoding latency on Tegra.
// will continue to not receive them now
sps.vuiParams.bitstreamRestriction = null;
}
// If we need to hack this SPS to say we're baseline, do so now // If the encoder didn't include VUI parameters in the SPS, add them now
if (needsBaselineSpsHack) { if (sps.vuiParams == null) {
LimeLog.info("Hacking SPS to baseline"); LimeLog.info("Adding VUI parameters");
sps.profileIdc = 66; sps.vuiParams = new VUIParameters();
savedSps = sps; }
}
// Patch the SPS constraint flags // GFE 2.5.11 started sending bitstream restrictions
doProfileSpecificSpsPatching(sps); if (sps.vuiParams.bitstreamRestriction == null) {
LimeLog.info("Adding bitstream restrictions");
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
sps.vuiParams.bitstreamRestriction.motionVectorsOverPicBoundariesFlag = true;
sps.vuiParams.bitstreamRestriction.maxBytesPerPicDenom = 2;
sps.vuiParams.bitstreamRestriction.maxBitsPerMbDenom = 1;
sps.vuiParams.bitstreamRestriction.log2MaxMvLengthHorizontal = 16;
sps.vuiParams.bitstreamRestriction.log2MaxMvLengthVertical = 16;
sps.vuiParams.bitstreamRestriction.numReorderFrames = 0;
}
else {
LimeLog.info("Patching bitstream restrictions");
}
// The H264Utils.writeSPS function safely handles // Some devices throw errors if maxDecFrameBuffering < numRefFrames
// Annex B NALUs (including NALUs with escape sequences) sps.vuiParams.bitstreamRestriction.maxDecFrameBuffering = sps.numRefFrames;
ByteBuffer escapedNalu = H264Utils.writeSPS(sps, decodeUnitLength);
// Batch this to submit together with PPS // These values are the defaults for the fields, but they are more aggressive
spsBuffer = new byte[startSeqLen + 1 + escapedNalu.limit()]; // than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
System.arraycopy(decodeUnitData, 0, spsBuffer, 0, startSeqLen + 1); // We'll leave these alone for "modern" devices just in case they care.
escapedNalu.get(spsBuffer, startSeqLen + 1, escapedNalu.limit()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return MoonBridge.DR_OK; sps.vuiParams.bitstreamRestriction.maxBytesPerPicDenom = 2;
} sps.vuiParams.bitstreamRestriction.maxBitsPerMbDenom = 1;
else if (decodeUnitType == MoonBridge.BUFFER_TYPE_VPS) { }
numVpsIn++;
// Batch this to submit together with SPS and PPS per AOSP docs // log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
vpsBuffer = new byte[decodeUnitLength]; // conservative values by GFE 2.5.11. We'll let those values stand.
System.arraycopy(decodeUnitData, 0, vpsBuffer, 0, decodeUnitLength); }
return MoonBridge.DR_OK; else if (sps.vuiParams != null) {
} // Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11
// Only the HEVC SPS hits this path (H.264 is handled above) // will continue to not receive them now
else if (decodeUnitType == MoonBridge.BUFFER_TYPE_SPS) { sps.vuiParams.bitstreamRestriction = null;
numSpsIn++;
// Batch this to submit together with VPS and PPS per AOSP docs
spsBuffer = new byte[decodeUnitLength];
System.arraycopy(decodeUnitData, 0, spsBuffer, 0, decodeUnitLength);
return MoonBridge.DR_OK;
}
else if (decodeUnitType == MoonBridge.BUFFER_TYPE_PPS) {
numPpsIn++;
// If this is the first CSD blob or we aren't supporting
// fused IDR frames, we will submit the CSD blob in a
// separate input buffer.
if (!submittedCsd || !fusedIdrFrame) {
if (!fetchNextInputBuffer()) {
return MoonBridge.DR_NEED_IDR;
} }
// When we get the PPS, submit the VPS and SPS together with // If we need to hack this SPS to say we're baseline, do so now
// the PPS, as required by AOSP docs on use of MediaCodec. if (needsBaselineSpsHack) {
if (vpsBuffer != null) { LimeLog.info("Hacking SPS to baseline");
nextInputBuffer.put(vpsBuffer); sps.profileIdc = 66;
} savedSps = sps;
if (spsBuffer != null) {
nextInputBuffer.put(spsBuffer);
} }
// This is the CSD blob // Patch the SPS constraint flags
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG; doProfileSpecificSpsPatching(sps);
timestampUs = 0;
}
else {
// Batch this to submit together with the next I-frame
ppsBuffer = new byte[decodeUnitLength];
System.arraycopy(decodeUnitData, 0, ppsBuffer, 0, decodeUnitLength);
// Next call will be I-frame data // The H264Utils.writeSPS function safely handles
submitCsdNextCall = true; // Annex B NALUs (including NALUs with escape sequences)
ByteBuffer escapedNalu = H264Utils.writeSPS(sps, decodeUnitLength);
// Construct the patched SPS
byte[] naluBuffer = new byte[startSeqLen + 1 + escapedNalu.limit()];
System.arraycopy(decodeUnitData, 0, naluBuffer, 0, startSeqLen + 1);
escapedNalu.get(naluBuffer, startSeqLen + 1, escapedNalu.limit());
// Batch this to submit together with other CSD per AOSP docs
spsBuffers.add(naluBuffer);
return MoonBridge.DR_OK; return MoonBridge.DR_OK;
} }
} else if (decodeUnitType == MoonBridge.BUFFER_TYPE_VPS) {
else { numVpsIn++;
if (frameHostProcessingLatency != 0) {
if (activeWindowVideoStats.minHostProcessingLatency != 0) { // Batch this to submit together with other CSD per AOSP docs
activeWindowVideoStats.minHostProcessingLatency = (char) Math.min(activeWindowVideoStats.minHostProcessingLatency, frameHostProcessingLatency); byte[] naluBuffer = new byte[decodeUnitLength];
} else { System.arraycopy(decodeUnitData, 0, naluBuffer, 0, decodeUnitLength);
activeWindowVideoStats.minHostProcessingLatency = frameHostProcessingLatency; vpsBuffers.add(naluBuffer);
return MoonBridge.DR_OK;
}
// Only the HEVC SPS hits this path (H.264 is handled above)
else if (decodeUnitType == MoonBridge.BUFFER_TYPE_SPS) {
numSpsIn++;
// Batch this to submit together with other CSD per AOSP docs
byte[] naluBuffer = new byte[decodeUnitLength];
System.arraycopy(decodeUnitData, 0, naluBuffer, 0, decodeUnitLength);
spsBuffers.add(naluBuffer);
return MoonBridge.DR_OK;
}
else if (decodeUnitType == MoonBridge.BUFFER_TYPE_PPS) {
numPpsIn++;
// Batch this to submit together with other CSD per AOSP docs
byte[] naluBuffer = new byte[decodeUnitLength];
System.arraycopy(decodeUnitData, 0, naluBuffer, 0, decodeUnitLength);
ppsBuffers.add(naluBuffer);
return MoonBridge.DR_OK;
}
else if ((videoFormat & (MoonBridge.VIDEO_FORMAT_MASK_H264 | MoonBridge.VIDEO_FORMAT_MASK_H265)) != 0) {
// If this is the first CSD blob or we aren't supporting fused IDR frames, we will
// submit the CSD blob in a separate input buffer for each IDR frame.
if (!submittedCsd || !fusedIdrFrame) {
if (!fetchNextInputBuffer()) {
return MoonBridge.DR_NEED_IDR;
}
// Submit all CSD when we receive the first non-CSD blob in an IDR frame
for (byte[] vpsBuffer : vpsBuffers) {
nextInputBuffer.put(vpsBuffer);
}
for (byte[] spsBuffer : spsBuffers) {
nextInputBuffer.put(spsBuffer);
}
for (byte[] ppsBuffer : ppsBuffers) {
nextInputBuffer.put(ppsBuffer);
}
if (!queueNextInputBuffer(0, MediaCodec.BUFFER_FLAG_CODEC_CONFIG)) {
return MoonBridge.DR_NEED_IDR;
}
// Remember that we already submitted CSD for this frame, so we don't do it
// again in the fused IDR case below.
csdSubmittedForThisFrame = true;
// Remember that we submitted CSD globally for this MediaCodec instance
submittedCsd = true;
if (needsBaselineSpsHack) {
needsBaselineSpsHack = false;
if (!replaySps()) {
return MoonBridge.DR_NEED_IDR;
}
LimeLog.info("SPS replay complete");
}
} }
activeWindowVideoStats.framesWithHostProcessingLatency += 1;
} }
activeWindowVideoStats.maxHostProcessingLatency = (char) Math.max(activeWindowVideoStats.maxHostProcessingLatency, frameHostProcessingLatency); }
activeWindowVideoStats.totalHostProcessingLatency += frameHostProcessingLatency;
activeWindowVideoStats.totalFramesReceived++; if (frameHostProcessingLatency != 0) {
activeWindowVideoStats.totalFrames++; if (activeWindowVideoStats.minHostProcessingLatency != 0) {
activeWindowVideoStats.minHostProcessingLatency = (char) Math.min(activeWindowVideoStats.minHostProcessingLatency, frameHostProcessingLatency);
if (!FRAME_RENDER_TIME_ONLY) { } else {
// Count time from first packet received to enqueue time as receive time activeWindowVideoStats.minHostProcessingLatency = frameHostProcessingLatency;
// We will count DU queue time as part of decoding, because it is directly
// caused by a slow decoder.
activeWindowVideoStats.totalTimeMs += enqueueTimeMs - receiveTimeMs;
} }
activeWindowVideoStats.framesWithHostProcessingLatency += 1;
}
activeWindowVideoStats.maxHostProcessingLatency = (char) Math.max(activeWindowVideoStats.maxHostProcessingLatency, frameHostProcessingLatency);
activeWindowVideoStats.totalHostProcessingLatency += frameHostProcessingLatency;
if (!fetchNextInputBuffer()) { activeWindowVideoStats.totalFramesReceived++;
return MoonBridge.DR_NEED_IDR; activeWindowVideoStats.totalFrames++;
}
if (submitCsdNextCall) { if (!FRAME_RENDER_TIME_ONLY) {
if (vpsBuffer != null) { // Count time from first packet received to enqueue time as receive time
// We will count DU queue time as part of decoding, because it is directly
// caused by a slow decoder.
activeWindowVideoStats.totalTimeMs += enqueueTimeMs - receiveTimeMs;
}
if (!fetchNextInputBuffer()) {
return MoonBridge.DR_NEED_IDR;
}
int codecFlags = 0;
if (frameType == MoonBridge.FRAME_TYPE_IDR) {
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
// If we are using fused IDR frames, submit the CSD with each IDR frame
if (fusedIdrFrame && !csdSubmittedForThisFrame) {
for (byte[] vpsBuffer : vpsBuffers) {
nextInputBuffer.put(vpsBuffer); nextInputBuffer.put(vpsBuffer);
} }
if (spsBuffer != null) { for (byte[] spsBuffer : spsBuffers) {
nextInputBuffer.put(spsBuffer); nextInputBuffer.put(spsBuffer);
} }
if (ppsBuffer != null) { for (byte[] ppsBuffer : ppsBuffers) {
nextInputBuffer.put(ppsBuffer); nextInputBuffer.put(ppsBuffer);
} }
submitCsdNextCall = false;
} }
if (frameType == MoonBridge.FRAME_TYPE_IDR) {
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
}
timestampUs = enqueueTimeMs * 1000;
if (timestampUs <= lastTimestampUs) {
// We can't submit multiple buffers with the same timestamp
// so bump it up by one before queuing
timestampUs = lastTimestampUs + 1;
}
lastTimestampUs = timestampUs;
numFramesIn++;
} }
long timestampUs = enqueueTimeMs * 1000;
if (timestampUs <= lastTimestampUs) {
// We can't submit multiple buffers with the same timestamp
// so bump it up by one before queuing
timestampUs = lastTimestampUs + 1;
}
lastTimestampUs = timestampUs;
numFramesIn++;
if (decodeUnitLength > nextInputBuffer.limit() - nextInputBuffer.position()) { if (decodeUnitLength > nextInputBuffer.limit() - nextInputBuffer.position()) {
IllegalArgumentException exception = new IllegalArgumentException( IllegalArgumentException exception = new IllegalArgumentException(
"Decode unit length "+decodeUnitLength+" too large for input buffer "+nextInputBuffer.limit()); "Decode unit length "+decodeUnitLength+" too large for input buffer "+nextInputBuffer.limit());
@@ -1723,20 +1749,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
return MoonBridge.DR_NEED_IDR; return MoonBridge.DR_NEED_IDR;
} }
if ((codecFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
submittedCsd = true;
if (needsBaselineSpsHack) {
needsBaselineSpsHack = false;
if (!replaySps()) {
return MoonBridge.DR_NEED_IDR;
}
LimeLog.info("SPS replay complete");
}
}
return MoonBridge.DR_OK; return MoonBridge.DR_OK;
} }
@@ -503,6 +503,15 @@ public class PreferenceConfiguration {
prefs.edit().putBoolean(SMALL_ICONS_PREF_STRING, getDefaultSmallMode(context)).apply(); prefs.edit().putBoolean(SMALL_ICONS_PREF_STRING, getDefaultSmallMode(context)).apply();
} }
if (!prefs.contains(GAMEPAD_MOTION_SENSORS_PREF_STRING) && Build.VERSION.SDK_INT == Build.VERSION_CODES.S) {
// Android 12 has a nasty bug that causes crashes when the app touches the InputDevice's
// associated InputDeviceSensorManager (just calling getSensorManager() is enough).
// As a workaround, we will override the default value for the gamepad motion sensor
// option to disabled on Android 12 to reduce the impact of this bug.
// https://cs.android.com/android/_/android/platform/frameworks/base/+/8970010a5e9f3dc5c069f56b4147552accfcbbeb
prefs.edit().putBoolean(GAMEPAD_MOTION_SENSORS_PREF_STRING, false).apply();
}
// This must happen after the preferences migration to ensure the preferences are populated // This must happen after the preferences migration to ensure the preferences are populated
config.bitrate = prefs.getInt(BITRATE_PREF_STRING, prefs.getInt(BITRATE_PREF_OLD_STRING, 0) * 1000); config.bitrate = prefs.getInt(BITRATE_PREF_STRING, prefs.getInt(BITRATE_PREF_OLD_STRING, 0) * 1000);
if (config.bitrate == 0) { if (config.bitrate == 0) {
@@ -190,8 +190,13 @@ public class StreamSettings extends Activity {
} }
private void addNativeFrameRateEntry(float framerate) { private void addNativeFrameRateEntry(float framerate) {
int frameRateRounded = Math.round(framerate);
if (frameRateRounded == 0) {
return;
}
ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.FPS_PREF_STRING); 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) + String fpsName = getResources().getString(R.string.resolution_prefix_native) +
" (" + fpsValue + " " + getResources().getString(R.string.fps_suffix_fps) + ")"; " (" + 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 // Hide non-supported resolution/FPS combinations
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Display display = getActivity().getWindowManager().getDefaultDisplay();
int maxSupportedResW = 0; int maxSupportedResW = 0;
// Add a native resolution with any insets included for users that don't want content // 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) { 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() // getRealMetrics() function (unlike the lies that getWidth() and getHeight()
// tell to us). // tell to us).
DisplayMetrics metrics = new DisplayMetrics(); DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getRealMetrics(metrics); display.getRealMetrics(metrics);
int width = Math.max(metrics.widthPixels, metrics.heightPixels); int width = Math.max(metrics.widthPixels, metrics.heightPixels);
int height = Math.min(metrics.widthPixels, metrics.heightPixels); int height = Math.min(metrics.widthPixels, metrics.heightPixels);
addNativeResolutionEntries(width, height, false); addNativeResolutionEntries(width, height, false);
@@ -516,7 +520,6 @@ public class StreamSettings extends Activity {
else { else {
// On Android 4.1, we have to resort to reflection to invoke hidden APIs // On Android 4.1, we have to resort to reflection to invoke hidden APIs
// to get the real screen dimensions. // to get the real screen dimensions.
Display display = getActivity().getWindowManager().getDefaultDisplay();
try { try {
Method getRawHeightFunc = Display.class.getMethod("getRawHeight"); Method getRawHeightFunc = Display.class.getMethod("getRawHeight");
Method getRawWidthFunc = Display.class.getMethod("getRawWidth"); Method getRawWidthFunc = Display.class.getMethod("getRawWidth");
@@ -607,7 +610,6 @@ public class StreamSettings extends Activity {
category.removePreference(findPreference("checkbox_enable_hdr")); category.removePreference(findPreference("checkbox_enable_hdr"));
} }
else { else {
Display display = getActivity().getWindowManager().getDefaultDisplay();
Display.HdrCapabilities hdrCaps = display.getHdrCapabilities(); Display.HdrCapabilities hdrCaps = display.getHdrCapabilities();
// We must now ensure our display is compatible with HDR10 // We must now ensure our display is compatible with HDR10
+10 -1
View File
@@ -79,7 +79,7 @@
<string name="conn_terminated_title">Verbindung beendet</string> <string name="conn_terminated_title">Verbindung beendet</string>
<string name="conn_terminated_msg">Die Verbindung wurde beendet</string> <string name="conn_terminated_msg">Die Verbindung wurde beendet</string>
<!-- General strings --> <!-- General strings -->
<string name="ip_hint">IP-Adresse des GeForce Host</string> <string name="ip_hint">IP-Adresse des GeForce Hosts</string>
<string name="searching_pc">Suche nach Hosts auf denen GeForce Experience aktiv ist… <string name="searching_pc">Suche nach Hosts auf denen GeForce Experience aktiv ist…
\n \n
\nStelle sicher, dass GameStream in den GeForce Experience SHIELD-Einstellungen aktiviert ist.</string> \nStelle sicher, dass GameStream in den GeForce Experience SHIELD-Einstellungen aktiviert ist.</string>
@@ -261,4 +261,13 @@
<string name="frame_conversion_error">Der Host-PC hat einen schwerwiegenden Videocodierungsfehler gemeldet. <string name="frame_conversion_error">Der Host-PC hat einen schwerwiegenden Videocodierungsfehler gemeldet.
\n \n
\nVersuchen Sie, den HDR-Modus zu deaktivieren, die Streaming-Auflösung zu ändern oder die Bildschirmauflösung des Host-PCs zu ändern.</string> \nVersuchen Sie, den HDR-Modus zu deaktivieren, die Streaming-Auflösung zu ändern oder die Bildschirmauflösung des Host-PCs zu ändern.</string>
<string name="title_native_fps_dialog">Native FPS Warnung</string>
<string name="videoformat_av1always">Immer AV1 verwenden (Experimentell)</string>
<string name="title_checkbox_gamepad_motion_sensors">Erlaube Benutzen von Gamepad Bewegungssensoren</string>
<string name="videoformat_h264always">Immer H.264 verwenden</string>
<string name="fps_suffix_fps">FPS</string>
<string name="category_gamepad_settings">Gamepad Einstellungen</string>
<string name="pair_pairing_help">Wenn dein Host PC Sunshine verwendet, navigiere zur Sunshine Web UI und gebe dort die PIN ein.</string>
<string name="title_checkbox_gamepad_touchpad_as_mouse">Die Maus immer mit dem Touchpad steuern</string>
<string name="perf_overlay_hostprocessinglatency">Host Verarbeitungslatenz min/max/avg: %1$.1f/%2$.1f/%3$.1f ms</string>
</resources> </resources>
+2 -2
View File
@@ -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="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="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="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="title_seekbar_bitrate">Bitrate vidéo</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="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_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_disable_warnings">Désactiver les messages d\'avertissement</string>
<string name="title_checkbox_enable_pip">Activer le mode observateur dans l\'image</string> <string name="title_checkbox_enable_pip">Activer le mode observateur dans l\'image</string>
+30 -15
View File
@@ -23,7 +23,7 @@
<string name="pair_already_in_progress">Accoppiamento già in corso</string> <string name="pair_already_in_progress">Accoppiamento già in corso</string>
<!-- WOL messages --> <!-- WOL messages -->
<string name="wol_pc_online">PC già avviato</string> <string name="wol_pc_online">PC già avviato</string>
<string name="wol_no_mac">Impossibile risvegliare il PC perché GFE non ha inviato nessun indirizzo MAC</string> <string name="wol_no_mac">Impossibile risvegliare il PC perché non è stato salvato nessun indirizzo MAC</string>
<string name="wol_waking_pc">Risveglio PC…</string> <string name="wol_waking_pc">Risveglio PC…</string>
<string name="wol_waking_msg">Il PC potrebbe impiegare qualche secondo per risvegliarsi. <string name="wol_waking_msg">Il PC potrebbe impiegare qualche secondo per risvegliarsi.
Se non succede niente, assicurati che l\'opzione Wake-On-LAN sia configurata correttamente. Se non succede niente, assicurati che l\'opzione Wake-On-LAN sia configurata correttamente.
@@ -33,7 +33,7 @@
<string name="unpairing">Disaccoppiamento…</string> <string name="unpairing">Disaccoppiamento…</string>
<string name="unpair_success">Disaccoppiato con successo</string> <string name="unpair_success">Disaccoppiato con successo</string>
<string name="unpair_fail">Disaccoppiamento fallito</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 --> <!-- Errors -->
<string name="error_pc_offline">PC offline</string> <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> <string name="error_manager_not_running">Il servizio ComputerManager non è avviato. Attendi qualche secondo o riavvia l\'applicazione.</string>
@@ -60,8 +60,9 @@
<string name="conn_terminated_msg">La connessione è stata interrotta</string> <string name="conn_terminated_msg">La connessione è stata interrotta</string>
<!-- General strings --> <!-- General strings -->
<string name="ip_hint">Indirizzo IP del PC</string> <string name="ip_hint">Indirizzo IP del PC</string>
<string name="searching_pc">Ricerca di PC con GameStream avviato…\n\n <string name="searching_pc">Ricerca di PC nella rete locale…
Assicurati che GameStream sia abilitato nelle impostazioni SHIELD di GeForce Experience.</string> \n
\n Assicurati che Sunshine sia avviato o che GameStream sia abilitato nelle impostazioni SHIELD di GeForce Experience.</string>
<string name="yes">Sì</string> <string name="yes">Sì</string>
<string name="no">No</string> <string name="no">No</string>
<string name="lost_connection">Connessione con il PC persa</string> <string name="lost_connection">Connessione con il PC persa</string>
@@ -90,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> <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 --> <!-- Preferences -->
<string name="category_basic_settings">Impostazioni generali</string> <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="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="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> <string name="summary_seekbar_bitrate">Aumentare per migliorare la qualità dell\'immagine. Diminuire per aumentare le prestazioni su reti più lente.</string>
@@ -133,10 +134,10 @@
<string name="category_advanced_settings">Impostazioni avanzate</string> <string name="category_advanced_settings">Impostazioni avanzate</string>
<string name="title_disable_frame_drop">Non saltare i fotogrammi</string> <string name="title_disable_frame_drop">Non saltare i fotogrammi</string>
<string name="summary_disable_frame_drop">Potrebbe ridurre il micro-stuttering su alcuni dispositivi, ma può aumentare la latenza</string> <string name="summary_disable_frame_drop">Potrebbe ridurre il micro-stuttering su alcuni dispositivi, ma può aumentare la latenza</string>
<string name="title_video_format">Modifica impostazioni HEVC</string> <string name="title_video_format">Modifica impostazioni del codec</string>
<string name="summary_video_format">HEVC riduce i requisiti di larghezza di banda video ma richiede un dispositivo molto recente</string> <string name="summary_video_format">I nuovi codec possono ridurre i requisiti di larghezza di banda video se il tuo dispositivo li supporta. La scelta del codec può essere ignorata se non è supportato dal software dell\'host o dalla GPU.</string>
<string name="title_enable_hdr">Abilita HDR (sperimentale)</string> <string name="title_enable_hdr">Abilita HDR (sperimentale)</string>
<string name="summary_enable_hdr">Utilizza l\'HDR quando il gioco e la scheda video del PC lo supportano. L\'HDR richiede una scheda video serie GTX 1000 o sucessive.</string> <string name="summary_enable_hdr">Utilizza l\'HDR quando il gioco e la scheda video del PC lo supportano. L\'HDR richiede una scheda video col supporto dell\'encoding HEVC Main 10.</string>
<string name="suffix_osc_opacity">%</string> <string name="suffix_osc_opacity">%</string>
<string name="pcview_menu_header_online">Online</string> <string name="pcview_menu_header_online">Online</string>
<string name="scut_pc_not_found">PC non trovato</string> <string name="scut_pc_not_found">PC non trovato</string>
@@ -161,7 +162,7 @@
<string name="perf_overlay_dectime">Tempo medio di decodifica: %1$.2f ms</string> <string name="perf_overlay_dectime">Tempo medio di decodifica: %1$.2f ms</string>
<string name="perf_overlay_streamdetails">Flusso video: %1$s %2$.2f FPS</string> <string name="perf_overlay_streamdetails">Flusso video: %1$s %2$.2f FPS</string>
<string name="nettest_title_waiting">Prova della Connessione di Rete</string> <string name="nettest_title_waiting">Prova della Connessione di Rete</string>
<string name="nettest_text_waiting">Moonlight sta testando la tua connessione di rete per controllare se NVIDIA GameStream sia bloccato. <string name="nettest_text_waiting">Moonlight sta testando la tua connessione di rete per controllare se ogni porta richiesta sia bloccata.
\n \n
\nPotrebbero volerci alcuni secondi…</string> \nPotrebbero volerci alcuni secondi…</string>
<string name="nettest_title_done">Test della Rete Completato</string> <string name="nettest_title_done">Test della Rete Completato</string>
@@ -226,18 +227,16 @@
<string name="no_frame_received_error">La tua connessione di rete non sta funzionando bene. Riduci il bitrate video o prova ad usare una connessione più veloce.</string> <string name="no_frame_received_error">La tua connessione di rete non sta funzionando bene. Riduci il bitrate video o prova ad usare una connessione più veloce.</string>
<string name="early_termination_error">Qualcosa è andato storto sul PC sorgente mentre la trasmissione veniva avviata. <string name="early_termination_error">Qualcosa è andato storto sul PC sorgente mentre la trasmissione veniva avviata.
\n \n
\nAssicurati di non avere nessun contenuto protetto da DRM aperto sul PC sorgente. Puoi anche provare a spengere e riaccendere il PC sorgente. \nAssicurati di non avere nessun contenuto protetto da DRM aperto sul PC sorgente. Puoi anche provare a spengere e riaccendere il PC sorgente.</string>
\n
\nSe il problema persiste, prova a installare di nuovo i driver della scheda video e GeForce Experience.</string>
<string name="summary_unlock_fps">Trasmettere a 90 o 120 FPS potrebbe ridurre il ritardo su dispositivi di fascia alta, ma può provocare ritardi i instabilità sui dispositivi che non lo supportano</string> <string name="summary_unlock_fps">Trasmettere a 90 o 120 FPS potrebbe ridurre il ritardo su dispositivi di fascia alta, ma può provocare ritardi i instabilità sui dispositivi che non lo supportano</string>
<string name="summary_enable_post_stream_toast">Molstra un messaggio con informazioni sulla latenza dopo che la trasmissione è conclusa</string> <string name="summary_enable_post_stream_toast">Molstra un messaggio con informazioni sulla latenza dopo che la trasmissione è conclusa</string>
<string name="text_native_res_dialog">Le impostazioni di risoluzione nativa non sono ufficialmente supportate da GeForce Experience, quindi non cambierà automaticamente la risoluzione del monitor del computer sorgente. Dovrai cambiarla manualmente all\'interno del gioco. <string name="text_native_res_dialog">Le impostazioni di risoluzione nativa e/o di FPS possono non essere supportate dal server per lo streaming. Dovrai aggiungerle manualmente all\'interno delle impostazioni del PC o del server.
\n \n
\nSe scegli di create una risoluzione personalizzata nel Pannello di Controllo NVIDIA per copiare la risoluzione del tuo dispositivo, assicurati di aver letto e capito il messaggio di attenzione di NVIDIA a proposito dei possibili danni al monitor, all\'instabilità del PC e altri potenziali problemi. \nSe scegli di create una risoluzione personalizzata nel Pannello di Controllo NVIDIA per copiare la risoluzione dello schermo, assicurati di aver letto e capito il messaggio di attenzione di NVIDIA a proposito dei possibili danni al monitor, all\'instabilità del PC e altri potenziali problemi.
\n \n
\nNon saremo responsabili per qualsiasi problema risultante dalla creazione di una risoluzione personalizzata sul tuo PC. \nNon saremo responsabili per qualsiasi problema risultante dalla creazione di una risoluzione personalizzata sul tuo PC.
\n \n
\nInfine, il tuo dispositivo o PC sorgente potrebbero non supportare la trasmissione alla risoluzione nativa. Se ciò non funzionasse sul tuo dispositivo, sfortunatamente non ci sono altre soluzioni.</string> \nPuò capitare che il tuo monitor potrebbe non supportare una risoluzione dello schermo richiesta. In questo caso, prova a impostare un monitor virtuale. Infine, se il tuo dispositivo o PC sorgente non supportano la trasmissione ad una specifica risoluzione dello schermo o della frequenza di aggiornamento, sfortunatamente non ci sono altre soluzioni.</string>
<string name="perf_overlay_incomingfps">Frame rate in ingresso dalla rete: %1$.2f FPS</string> <string name="perf_overlay_incomingfps">Frame rate in ingresso dalla rete: %1$.2f FPS</string>
<string name="perf_overlay_renderingfps">Frame rate renderizzato: %1$.2f FPS</string> <string name="perf_overlay_renderingfps">Frame rate renderizzato: %1$.2f FPS</string>
<string name="perf_overlay_netdrops">Frame scartati dalla tua connessione di rete: %1$.2f%%</string> <string name="perf_overlay_netdrops">Frame scartati dalla tua connessione di rete: %1$.2f%%</string>
@@ -264,4 +263,20 @@
<string name="title_checkbox_reduce_refresh_rate">Consenti riduzione della frequenza di aggiornamento</string> <string name="title_checkbox_reduce_refresh_rate">Consenti riduzione della frequenza di aggiornamento</string>
<string name="summary_full_range">Ciò causerà la perdita di dettagli in aree chiare e scure se il dispositivo non visualizza correttamente i contenuti video a gamma completa.</string> <string name="summary_full_range">Ciò causerà la perdita di dettagli in aree chiare e scure se il dispositivo non visualizza correttamente i contenuti video a gamma completa.</string>
<string name="title_full_range">Forza video full range (sperimentale)</string> <string name="title_full_range">Forza video full range (sperimentale)</string>
<string name="title_native_fps_dialog">Avviso FPS nativi</string>
<string name="videoformat_av1always">Preferisci AV1 (Sperimentale)</string>
<string name="pcview_menu_eol">Terminazione del servizio NVIDIA GameStream</string>
<string name="title_checkbox_gamepad_motion_sensors">Permetti l\'uso dei sensori giroscopici del gamepad</string>
<string name="summary_checkbox_gamepad_touchpad_as_mouse">Forza l\'input del touchpad del gamepad a controllare il mouse dell\'host, anche quando si sta emulando un gamepad col touchpad.</string>
<string name="toast_controller_type_changed">Il tipo di gamepad potrebbe cambiare se è attiva l\'emulazione dei sensori giroscopici</string>
<string name="summary_checkbox_gamepad_motion_sensors">Abilita per gli host supportati la trasmissione dei dati dai sensori giroscopici quando si simula un gamepad con sensori giroscopici. Disabilitandolo può ridurre il consumo di batteria e la rete richiesta per lo stream se i sensori giroscopici non vengono utilizzati nel gioco.</string>
<string name="summary_checkbox_gamepad_motion_fallback">Usa i sensori giroscopici integrati nel tuo dispositivo se il tuo gamepad non possiede i sensori giroscopici o non sono compatibili con la tua versione Android.
\nNota: Abilitare questa opzione farà apparire il tuo gamepad come un controller Playstation all\'host.</string>
<string name="videoformat_h264always">Preferisci H.264</string>
<string name="fps_suffix_fps">FPS</string>
<string name="category_gamepad_settings">Impostazioni del gamepad</string>
<string name="title_checkbox_gamepad_motion_fallback">Emula il supporto dei sensori giroscopici del gamepad</string>
<string name="pair_pairing_help">Se il tuo PC host sta eseguendo Sunshine, vai nella pagina web locale di Sunshine ed inserisci il PIN.</string>
<string name="title_checkbox_gamepad_touchpad_as_mouse">Controlla sempre il mouse col touchpad</string>
<string name="perf_overlay_hostprocessinglatency">Latenza di codifica da parte dell\'host min/max/average: %1$.1f/%2$.1f/%3$.1f ms</string>
</resources> </resources>
@@ -0,0 +1,5 @@
- Added Select+L1 gamepad button combo to act as the touchpad button when emulating a PS4 controller using built-in device motion sensors
- Disabled gamepad motion sensors by default on Android 12 as a workaround for an Android 12 bug that randomly crashes Moonlight
- Fixed analog sticks not centering properly in rare cases
- Adjusted bitrate handling of AV1 to be consistent with HEVC
- Fixed handling of some unusual H.264 and HEVC streams
@@ -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