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
+3 -3
View File
@@ -11,8 +11,8 @@ android {
minSdk 16
targetSdk 34
versionName "12.0"
versionCode = 311
versionName "12.0.2"
versionCode = 313
// Generate native debug symbols to allow Google Play to symbolicate our native crashes
ndk.debugSymbolLevel = 'FULL'
@@ -143,5 +143,5 @@ dependencies {
implementation 'com.squareup.okio:okio:1.17.5'
// 3.5.8 requires minSdk 19, uses StandardCharsets.UTF_8 internally
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 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,14 +468,14 @@ 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)
.setMaxPacketSize(1392)
.setRemoteConfiguration(StreamConfiguration.STREAM_CFG_AUTO) // NvConnection will perform LAN and VPN detection
.setHevcBitratePercentageMultiplier(75)
.setAv1BitratePercentageMultiplier(60)
.setAv1BitratePercentageMultiplier(75)
.setSupportedVideoFormats(supportedVideoFormats)
.setAttachedGamepadMask(gamepadMask)
.setClientRefreshRateX100((int)(displayRefreshRate * 100))
@@ -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
+5 -5
View File
@@ -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;
@@ -63,6 +63,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private static final int EMULATING_SPECIAL = 0x1;
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
@@ -113,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;
@@ -133,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
@@ -204,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) {
@@ -259,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();
@@ -292,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();
@@ -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.
// We do this first because other codepaths below may override these defaults.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@@ -2019,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);
@@ -2079,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);
@@ -2190,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);
@@ -2251,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();
@@ -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);
if (context.pendingExit && context.inputMap == 0) {
@@ -2685,6 +2742,18 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
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
// otherwise we'll use Start+RB.
@@ -2872,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
@@ -2914,6 +2984,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public boolean hasMode;
public boolean hasPaddles;
public boolean hasShare;
public boolean needsClickpadEmulation;
// 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
@@ -2984,7 +3055,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return;
}
byte type = MoonBridge.LI_CTYPE_UNKNOWN;
byte type;
switch (inputDevice.getVendorId()) {
case 0x045e: // Microsoft
type = MoonBridge.LI_CTYPE_XBOX;
@@ -3038,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;
}
}
@@ -3067,10 +3139,18 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
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) {
// 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();
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
@@ -3093,7 +3173,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
conn.sendControllerArrivalEvent((byte)controllerNumber, getActiveControllerMask(),
type, supportedButtonFlags, capabilities);
reportedType, supportedButtonFlags, capabilities);
// After reporting arrival to the host, send initial battery state and begin monitoring
backgroundThreadHandler.post(batteryStateUpdateRunnable);
@@ -3123,6 +3203,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
this.sensorManager = deviceSensorManager;
}
// Copy state initialized in reportControllerArrival()
this.needsClickpadEmulation = oldContext.needsClickpadEmulation;
// Re-enable sensors on the new context
enableSensors();
@@ -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();
}
@@ -3,6 +3,7 @@ package com.limelight.binding.video;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
@@ -48,11 +49,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
private MediaCodecInfo hevcDecoder;
private MediaCodecInfo av1Decoder;
private byte[] vpsBuffer;
private byte[] spsBuffer;
private byte[] ppsBuffer;
private final ArrayList<byte[]> vpsBuffers = new ArrayList<>();
private final ArrayList<byte[]> spsBuffers = new ArrayList<>();
private final ArrayList<byte[]> ppsBuffers = new ArrayList<>();
private boolean submittedCsd;
private boolean submitCsdNextCall;
private byte[] currentHdrMetadata;
private int nextInputBufferIndex = -1;
@@ -544,10 +544,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
// After reconfiguration, we must resubmit CSD buffers
submittedCsd = false;
submitCsdNextCall = false;
vpsBuffer = null;
spsBuffer = null;
ppsBuffer = null;
vpsBuffers.clear();
spsBuffers.clear();
ppsBuffers.clear();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// This will contain the actual accepted input format attributes
@@ -1414,6 +1413,13 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
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;
// Flip stats windows roughly every second
@@ -1462,250 +1468,270 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
activeWindowVideoStats.measurementStartTimestamp = SystemClock.uptimeMillis();
}
long timestampUs;
int codecFlags = 0;
boolean csdSubmittedForThisFrame = false;
// H264 SPS
if (decodeUnitType == MoonBridge.BUFFER_TYPE_SPS && (videoFormat & MoonBridge.VIDEO_FORMAT_MASK_H264) != 0) {
numSpsIn++;
// IDR frames require special handling for CSD buffer submission
if (frameType == MoonBridge.FRAME_TYPE_IDR) {
// H264 SPS
if (decodeUnitType == MoonBridge.BUFFER_TYPE_SPS && (videoFormat & MoonBridge.VIDEO_FORMAT_MASK_H264) != 0) {
numSpsIn++;
ByteBuffer spsBuf = ByteBuffer.wrap(decodeUnitData);
int startSeqLen = decodeUnitData[2] == 0x01 ? 3 : 4;
ByteBuffer spsBuf = ByteBuffer.wrap(decodeUnitData);
int startSeqLen = decodeUnitData[2] == 0x01 ? 3 : 4;
// Skip to the start of the NALU data
spsBuf.position(startSeqLen + 1);
// Skip to the start of the NALU data
spsBuf.position(startSeqLen + 1);
// The H264Utils.readSPS function safely handles
// Annex B NALUs (including NALUs with escape sequences)
SeqParameterSet sps = H264Utils.readSPS(spsBuf);
// The H264Utils.readSPS function safely handles
// Annex B NALUs (including NALUs with escape sequences)
SeqParameterSet sps = H264Utils.readSPS(spsBuf);
// 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
// for known resolution combinations. Reference frame invalidation may need
// these, so leave them be for those decoders.
if (!refFrameInvalidationActive) {
if (initialWidth <= 720 && initialHeight <= 480 && refreshRate <= 60) {
// Max 5 buffered frames at 720x480x60
LimeLog.info("Patching level_idc to 31");
sps.levelIdc = 31;
}
else if (initialWidth <= 1280 && initialHeight <= 720 && refreshRate <= 60) {
// Max 5 buffered frames at 1280x720x60
LimeLog.info("Patching level_idc to 32");
sps.levelIdc = 32;
}
else if (initialWidth <= 1920 && initialHeight <= 1080 && refreshRate <= 60) {
// Max 4 buffered frames at 1920x1080x64
LimeLog.info("Patching level_idc to 42");
sps.levelIdc = 42;
}
else {
// 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();
// 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
// for known resolution combinations. Reference frame invalidation may need
// these, so leave them be for those decoders.
if (!refFrameInvalidationActive) {
if (initialWidth <= 720 && initialHeight <= 480 && refreshRate <= 60) {
// Max 5 buffered frames at 720x480x60
LimeLog.info("Patching level_idc to 31");
sps.levelIdc = 31;
}
else if (initialWidth <= 1280 && initialHeight <= 720 && refreshRate <= 60) {
// Max 5 buffered frames at 1280x720x60
LimeLog.info("Patching level_idc to 32");
sps.levelIdc = 32;
}
else if (initialWidth <= 1920 && initialHeight <= 1080 && refreshRate <= 60) {
// Max 4 buffered frames at 1920x1080x64
LimeLog.info("Patching level_idc to 42");
sps.levelIdc = 42;
}
else {
// Leave the profile alone (currently 5.0)
}
}
// GFE 2.5.11 started sending bitstream restrictions
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");
// 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;
}
// Some devices throw errors if maxDecFrameBuffering < numRefFrames
sps.vuiParams.bitstreamRestriction.maxDecFrameBuffering = sps.numRefFrames;
// These values are the defaults for the fields, but they are more aggressive
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
// We'll leave these alone for "modern" devices just in case they care.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
sps.vuiParams.bitstreamRestriction.maxBytesPerPicDenom = 2;
sps.vuiParams.bitstreamRestriction.maxBitsPerMbDenom = 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;
}
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
// conservative values by GFE 2.5.11. We'll let those values stand.
}
else if (sps.vuiParams != null) {
// Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11
// will continue to not receive them now
sps.vuiParams.bitstreamRestriction = null;
}
// 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 we need to hack this SPS to say we're baseline, do so now
if (needsBaselineSpsHack) {
LimeLog.info("Hacking SPS to baseline");
sps.profileIdc = 66;
savedSps = sps;
}
// 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();
}
// Patch the SPS constraint flags
doProfileSpecificSpsPatching(sps);
// GFE 2.5.11 started sending bitstream restrictions
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
// Annex B NALUs (including NALUs with escape sequences)
ByteBuffer escapedNalu = H264Utils.writeSPS(sps, decodeUnitLength);
// Some devices throw errors if maxDecFrameBuffering < numRefFrames
sps.vuiParams.bitstreamRestriction.maxDecFrameBuffering = sps.numRefFrames;
// Batch this to submit together with PPS
spsBuffer = new byte[startSeqLen + 1 + escapedNalu.limit()];
System.arraycopy(decodeUnitData, 0, spsBuffer, 0, startSeqLen + 1);
escapedNalu.get(spsBuffer, startSeqLen + 1, escapedNalu.limit());
return MoonBridge.DR_OK;
}
else if (decodeUnitType == MoonBridge.BUFFER_TYPE_VPS) {
numVpsIn++;
// These values are the defaults for the fields, but they are more aggressive
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
// We'll leave these alone for "modern" devices just in case they care.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
sps.vuiParams.bitstreamRestriction.maxBytesPerPicDenom = 2;
sps.vuiParams.bitstreamRestriction.maxBitsPerMbDenom = 1;
}
// Batch this to submit together with SPS and PPS per AOSP docs
vpsBuffer = new byte[decodeUnitLength];
System.arraycopy(decodeUnitData, 0, vpsBuffer, 0, decodeUnitLength);
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 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;
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
// conservative values by GFE 2.5.11. We'll let those values stand.
}
else if (sps.vuiParams != null) {
// Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11
// will continue to not receive them now
sps.vuiParams.bitstreamRestriction = null;
}
// When we get the PPS, submit the VPS and SPS together with
// the PPS, as required by AOSP docs on use of MediaCodec.
if (vpsBuffer != null) {
nextInputBuffer.put(vpsBuffer);
}
if (spsBuffer != null) {
nextInputBuffer.put(spsBuffer);
// If we need to hack this SPS to say we're baseline, do so now
if (needsBaselineSpsHack) {
LimeLog.info("Hacking SPS to baseline");
sps.profileIdc = 66;
savedSps = sps;
}
// This is the CSD blob
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
timestampUs = 0;
}
else {
// Batch this to submit together with the next I-frame
ppsBuffer = new byte[decodeUnitLength];
System.arraycopy(decodeUnitData, 0, ppsBuffer, 0, decodeUnitLength);
// Patch the SPS constraint flags
doProfileSpecificSpsPatching(sps);
// Next call will be I-frame data
submitCsdNextCall = true;
// The H264Utils.writeSPS function safely handles
// 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;
}
}
else {
if (frameHostProcessingLatency != 0) {
if (activeWindowVideoStats.minHostProcessingLatency != 0) {
activeWindowVideoStats.minHostProcessingLatency = (char) Math.min(activeWindowVideoStats.minHostProcessingLatency, frameHostProcessingLatency);
} else {
activeWindowVideoStats.minHostProcessingLatency = frameHostProcessingLatency;
else if (decodeUnitType == MoonBridge.BUFFER_TYPE_VPS) {
numVpsIn++;
// Batch this to submit together with other CSD per AOSP docs
byte[] naluBuffer = new byte[decodeUnitLength];
System.arraycopy(decodeUnitData, 0, naluBuffer, 0, decodeUnitLength);
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++;
activeWindowVideoStats.totalFrames++;
if (!FRAME_RENDER_TIME_ONLY) {
// 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 (frameHostProcessingLatency != 0) {
if (activeWindowVideoStats.minHostProcessingLatency != 0) {
activeWindowVideoStats.minHostProcessingLatency = (char) Math.min(activeWindowVideoStats.minHostProcessingLatency, frameHostProcessingLatency);
} else {
activeWindowVideoStats.minHostProcessingLatency = frameHostProcessingLatency;
}
activeWindowVideoStats.framesWithHostProcessingLatency += 1;
}
activeWindowVideoStats.maxHostProcessingLatency = (char) Math.max(activeWindowVideoStats.maxHostProcessingLatency, frameHostProcessingLatency);
activeWindowVideoStats.totalHostProcessingLatency += frameHostProcessingLatency;
if (!fetchNextInputBuffer()) {
return MoonBridge.DR_NEED_IDR;
}
activeWindowVideoStats.totalFramesReceived++;
activeWindowVideoStats.totalFrames++;
if (submitCsdNextCall) {
if (vpsBuffer != null) {
if (!FRAME_RENDER_TIME_ONLY) {
// 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);
}
if (spsBuffer != null) {
for (byte[] spsBuffer : spsBuffers) {
nextInputBuffer.put(spsBuffer);
}
if (ppsBuffer != null) {
for (byte[] ppsBuffer : ppsBuffers) {
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()) {
IllegalArgumentException exception = new IllegalArgumentException(
"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;
}
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;
}
@@ -503,6 +503,15 @@ public class PreferenceConfiguration {
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
config.bitrate = prefs.getInt(BITRATE_PREF_STRING, prefs.getInt(BITRATE_PREF_OLD_STRING, 0) * 1000);
if (config.bitrate == 0) {
@@ -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
+10 -1
View File
@@ -79,7 +79,7 @@
<string name="conn_terminated_title">Verbindung beendet</string>
<string name="conn_terminated_msg">Die Verbindung wurde beendet</string>
<!-- 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…
\n
\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.
\n
\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>
+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="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é dimage. 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>
+30 -15
View File
@@ -23,7 +23,7 @@
<string name="pair_already_in_progress">Accoppiamento già in corso</string>
<!-- WOL messages -->
<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_msg">Il PC potrebbe impiegare qualche secondo per risvegliarsi.
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="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>
@@ -60,8 +60,9 @@
<string name="conn_terminated_msg">La connessione è stata interrotta</string>
<!-- General strings -->
<string name="ip_hint">Indirizzo IP del PC</string>
<string name="searching_pc">Ricerca di PC con GameStream avviato…\n\n
Assicurati che GameStream sia abilitato nelle impostazioni SHIELD di GeForce Experience.</string>
<string name="searching_pc">Ricerca di PC nella rete locale…
\n
\n Assicurati che Sunshine sia avviato o che GameStream sia abilitato nelle impostazioni SHIELD di GeForce Experience.</string>
<string name="yes"></string>
<string name="no">No</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>
<!-- 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>
@@ -133,10 +134,10 @@
<string name="category_advanced_settings">Impostazioni avanzate</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="title_video_format">Modifica impostazioni HEVC</string>
<string name="summary_video_format">HEVC riduce i requisiti di larghezza di banda video ma richiede un dispositivo molto recente</string>
<string name="title_video_format">Modifica impostazioni del codec</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="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="pcview_menu_header_online">Online</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_streamdetails">Flusso video: %1$s %2$.2f FPS</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
\nPotrebbero volerci alcuni secondi…</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="early_termination_error">Qualcosa è andato storto sul PC sorgente mentre la trasmissione veniva avviata.
\n
\nAssicurati di non avere nessun contenuto protetto da DRM aperto sul PC sorgente. Puoi anche provare a spengere e riaccendere il PC sorgente.
\n
\nSe il problema persiste, prova a installare di nuovo i driver della scheda video e GeForce Experience.</string>
\nAssicurati di non avere nessun contenuto protetto da DRM aperto sul PC sorgente. Puoi anche provare a spengere e riaccendere il PC sorgente.</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="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
\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
\nNon saremo responsabili per qualsiasi problema risultante dalla creazione di una risoluzione personalizzata sul tuo PC.
\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_renderingfps">Frame rate renderizzato: %1$.2f FPS</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="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_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>
@@ -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