mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-10-21 15:56:57 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0045a885b9 | ||
|
|
0b57f60454 | ||
|
|
f0857c7da2 | ||
|
|
15faa2e841 | ||
|
|
da103f7197 | ||
|
|
1d3e42f92e | ||
|
|
20ced841dd | ||
|
|
54ebd0a796 | ||
|
|
e636a7171b | ||
|
|
e8f847065b | ||
|
|
1c806bb572 | ||
|
|
963133598f | ||
|
|
fedaa74c47 | ||
|
|
e322baf1d7 | ||
|
|
173a07cb59 | ||
|
|
364afff860 | ||
|
|
1b59e61b8e | ||
|
|
b1f453f7ba | ||
|
|
175e842feb |
No files matched your search
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Follow the troubleshooting guide before reporting a bug
|
||||||
|
|
||||||
|
---
|
||||||
|
**READ ME FIRST!**
|
||||||
|
If you're here because something basic is not working (like gamepad input, video, or similar), it's probably something specific to your setup, so make sure you've gone through the Troubleshooting Guide first: https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting
|
||||||
|
|
||||||
|
If you still have trouble with basic functionality after following the guide, join our Discord server where there are many other volunteers who can help (or direct you back here if it looks like a Moonlight bug after all). https://moonlight-stream.org/discord
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Steps to reproduce**
|
||||||
|
Any special steps that are required for the bug to appear.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem. If the issue is related to video glitching or poor quality, please include screenshots.
|
||||||
|
|
||||||
|
**Affected games**
|
||||||
|
List the games you've tried that exhibit the issue. To see if the issue is game-specific, try streaming Steam Big Picture with Moonlight and see if the issue persists there.
|
||||||
|
|
||||||
|
**Other Moonlight clients**
|
||||||
|
- Does the issue occur when using Moonlight on PC or iOS?
|
||||||
|
|
||||||
|
**Moonlight settings (please complete the following information)**
|
||||||
|
- Have any settings been adjusted from defaults?
|
||||||
|
- If so, which settings have been changed?
|
||||||
|
- Does the problem still occur after reverting settings back to default?
|
||||||
|
|
||||||
|
**Gamepad-related issues (please complete if problem is gamepad-related)**
|
||||||
|
- Do you have any gamepads connected to your host PC directly?
|
||||||
|
- If gamepad input is not working, does it work if you use Moonlight's on-screen controls?
|
||||||
|
- Does the problem still remain if you stream the desktop and use https://html5gamepad.com to test your gamepad?
|
||||||
|
- Instructions for streaming the desktop can be found here: https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide
|
||||||
|
|
||||||
|
**Device details (please complete the following information)**
|
||||||
|
- Android version: [e.g. Android 10]
|
||||||
|
- Device model: [e.g. Samsung Galaxy S21]
|
||||||
|
|
||||||
|
**Server PC details (please complete the following information)**
|
||||||
|
- OS: [e.g. Windows 10 1809]
|
||||||
|
- GeForce Experience version: [e.g. 3.16.0.140]
|
||||||
|
- Nvidia GPU driver: [e.g. 417.35]
|
||||||
|
- Antivirus and firewall software: [e.g. Windows Defender and Windows Firewall]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Anything else you think may be relevant to the issue or special about your specific setup.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
language: android
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
git:
|
|
||||||
depth: 1
|
|
||||||
|
|
||||||
android:
|
|
||||||
components:
|
|
||||||
- tools
|
|
||||||
- platform-tools
|
|
||||||
- build-tools-30.0.0
|
|
||||||
- android-30
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- sdkmanager --list
|
|
||||||
|
|
||||||
install:
|
|
||||||
- yes | sdkmanager "ndk;21.0.6113669"
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Moonlight Android
|
# Moonlight Android
|
||||||
|
|
||||||
[](https://travis-ci.org/moonlight-stream/moonlight-android)
|
[](https://ci.appveyor.com/project/cgutman/moonlight-android/branch/master)
|
||||||
|
|
||||||
[Moonlight for Android](https://moonlight-stream.org) is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield.
|
[Moonlight for Android](https://moonlight-stream.org) is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -7,8 +7,8 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
|
|
||||||
versionName "9.8"
|
versionName "9.8.3"
|
||||||
versionCode = 251
|
versionCode = 254
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions "root"
|
flavorDimensions "root"
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".PcView"
|
android:name=".PcView"
|
||||||
|
android:exported="true"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:configChanges="mcc|mnc|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection">
|
android:configChanges="mcc|mnc|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -598,6 +598,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
public void onWindowFocusChanged(boolean hasFocus) {
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
super.onWindowFocusChanged(hasFocus);
|
super.onWindowFocusChanged(hasFocus);
|
||||||
|
|
||||||
|
// We can't guarantee the state of modifiers keys which may have
|
||||||
|
// lifted while focus was not on us. Clear the modifier state.
|
||||||
|
this.modifierFlags = 0;
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
// Capture is lost when focus is lost, so it must be requested again
|
// Capture is lost when focus is lost, so it must be requested again
|
||||||
// when focus is regained.
|
// when focus is regained.
|
||||||
@@ -630,6 +634,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// On M, we can explicitly set the optimal display mode
|
// On M, we can explicitly set the optimal display mode
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
Display.Mode bestMode = display.getMode();
|
Display.Mode bestMode = display.getMode();
|
||||||
|
boolean isNativeResolutionStream = PreferenceConfiguration.isNativeResolution(prefConfig.width, prefConfig.height);
|
||||||
boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate());
|
boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate());
|
||||||
for (Display.Mode candidate : display.getSupportedModes()) {
|
for (Display.Mode candidate : display.getSupportedModes()) {
|
||||||
boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate();
|
boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate();
|
||||||
@@ -641,14 +646,15 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
|
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
|
||||||
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
|
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
|
||||||
|
|
||||||
if (candidate.getPhysicalWidth() > 4096) {
|
if (candidate.getPhysicalWidth() > 4096 && prefConfig.width <= 4096) {
|
||||||
// Avoid resolutions options above 4K to be safe
|
// Avoid resolutions options above 4K to be safe
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On non-4K streams, we force the resolution to never change unless it's above
|
// On non-4K streams, we force the resolution to never change unless it's above
|
||||||
// 60 FPS, which may require a resolution reduction due to HDMI bandwidth limitations.
|
// 60 FPS, which may require a resolution reduction due to HDMI bandwidth limitations,
|
||||||
if (prefConfig.width < 3840 && prefConfig.fps <= 60) {
|
// or it's a native resolution stream.
|
||||||
|
if (prefConfig.width < 3840 && prefConfig.fps <= 60 && !isNativeResolutionStream) {
|
||||||
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
|
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
|
||||||
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
|
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -994,20 +1000,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte getModifierState(KeyEvent event) {
|
|
||||||
byte modifier = 0;
|
|
||||||
if (event.isShiftPressed()) {
|
|
||||||
modifier |= KeyboardPacket.MODIFIER_SHIFT;
|
|
||||||
}
|
|
||||||
if (event.isCtrlPressed()) {
|
|
||||||
modifier |= KeyboardPacket.MODIFIER_CTRL;
|
|
||||||
}
|
|
||||||
if (event.isAltPressed()) {
|
|
||||||
modifier |= KeyboardPacket.MODIFIER_ALT;
|
|
||||||
}
|
|
||||||
return modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte getModifierState() {
|
private byte getModifierState() {
|
||||||
return (byte) modifierFlags;
|
return (byte) modifierFlags;
|
||||||
}
|
}
|
||||||
@@ -1073,7 +1065,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte modifiers = getModifierState(event);
|
byte modifiers = getModifierState();
|
||||||
if (KeyboardTranslator.needsShift(event.getKeyCode())) {
|
if (KeyboardTranslator.needsShift(event.getKeyCode())) {
|
||||||
modifiers |= KeyboardPacket.MODIFIER_SHIFT;
|
modifiers |= KeyboardPacket.MODIFIER_SHIFT;
|
||||||
conn.sendKeyboardInput((short) 0x8010, KeyboardPacket.KEY_DOWN, modifiers);
|
conn.sendKeyboardInput((short) 0x8010, KeyboardPacket.KEY_DOWN, modifiers);
|
||||||
@@ -1137,13 +1129,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte modifiers = getModifierState(event);
|
byte modifiers = getModifierState();
|
||||||
if (KeyboardTranslator.needsShift(event.getKeyCode())) {
|
if (KeyboardTranslator.needsShift(event.getKeyCode())) {
|
||||||
modifiers |= KeyboardPacket.MODIFIER_SHIFT;
|
modifiers |= KeyboardPacket.MODIFIER_SHIFT;
|
||||||
}
|
}
|
||||||
conn.sendKeyboardInput(translated, KeyboardPacket.KEY_UP, modifiers);
|
conn.sendKeyboardInput(translated, KeyboardPacket.KEY_UP, modifiers);
|
||||||
if (KeyboardTranslator.needsShift(event.getKeyCode())) {
|
if (KeyboardTranslator.needsShift(event.getKeyCode())) {
|
||||||
conn.sendKeyboardInput((short) 0x8010, KeyboardPacket.KEY_UP, getModifierState(event));
|
conn.sendKeyboardInput((short) 0x8010, KeyboardPacket.KEY_UP, getModifierState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1185,6 +1177,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
{
|
{
|
||||||
// This case is for mice and non-finger touch devices
|
// This case is for mice and non-finger touch devices
|
||||||
if (event.getSource() == InputDevice.SOURCE_MOUSE ||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||||
|
event.getSource() == InputDevice.SOURCE_TOUCHPAD ||
|
||||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE ||
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE ||
|
||||||
(event.getPointerCount() >= 1 &&
|
(event.getPointerCount() >= 1 &&
|
||||||
(event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE ||
|
(event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE ||
|
||||||
|
|||||||
@@ -319,20 +319,23 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
|
|
||||||
// Add a header with PC status details
|
// Add a header with PC status details
|
||||||
menu.clearHeader();
|
menu.clearHeader();
|
||||||
|
String headerTitle = computer.details.name + " - ";
|
||||||
switch (computer.details.state)
|
switch (computer.details.state)
|
||||||
{
|
{
|
||||||
case ONLINE:
|
case ONLINE:
|
||||||
menu.setHeaderTitle(R.string.pcview_menu_header_online);
|
headerTitle += getResources().getString(R.string.pcview_menu_header_online);
|
||||||
break;
|
break;
|
||||||
case OFFLINE:
|
case OFFLINE:
|
||||||
menu.setHeaderIcon(R.drawable.ic_pc_offline);
|
menu.setHeaderIcon(R.drawable.ic_pc_offline);
|
||||||
menu.setHeaderTitle(R.string.pcview_menu_header_offline);
|
headerTitle += getResources().getString(R.string.pcview_menu_header_offline);
|
||||||
break;
|
break;
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
menu.setHeaderTitle(R.string.pcview_menu_header_unknown);
|
headerTitle += getResources().getString(R.string.pcview_menu_header_unknown);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.setHeaderTitle(headerTitle);
|
||||||
|
|
||||||
// Inflate the context menu
|
// Inflate the context menu
|
||||||
if (computer.details.state == ComputerDetails.State.OFFLINE ||
|
if (computer.details.state == ComputerDetails.State.OFFLINE ||
|
||||||
computer.details.state == ComputerDetails.State.UNKNOWN) {
|
computer.details.state == ComputerDetails.State.UNKNOWN) {
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
defaultContext.rightStickDeadzoneRadius = (float) stickDeadzone;
|
defaultContext.rightStickDeadzoneRadius = (float) stickDeadzone;
|
||||||
defaultContext.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
defaultContext.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
||||||
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
||||||
|
defaultContext.hatXAxis = MotionEvent.AXIS_HAT_X;
|
||||||
|
defaultContext.hatYAxis = MotionEvent.AXIS_HAT_Y;
|
||||||
defaultContext.controllerNumber = (short) 0;
|
defaultContext.controllerNumber = (short) 0;
|
||||||
defaultContext.assignedControllerNumber = true;
|
defaultContext.assignedControllerNumber = true;
|
||||||
defaultContext.external = false;
|
defaultContext.external = false;
|
||||||
@@ -194,6 +196,28 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK for https://issuetracker.google.com/issues/163120692
|
||||||
|
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
||||||
|
if (device.getId() == -1) {
|
||||||
|
// This "virtual" device could be input from any of the attached devices.
|
||||||
|
// Look to see if any gamepads are connected.
|
||||||
|
int[] ids = InputDevice.getDeviceIds();
|
||||||
|
for (int id : ids) {
|
||||||
|
InputDevice dev = InputDevice.getDevice(id);
|
||||||
|
if (dev == null) {
|
||||||
|
// This device was removed during enumeration
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are any gamepad devices connected, we'll
|
||||||
|
// report that this virtual device is a gamepad.
|
||||||
|
if (hasJoystickAxes(dev) || hasGamepadButtons(dev)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise, we'll try anything that claims to be a non-alphabetic keyboard
|
// Otherwise, we'll try anything that claims to be a non-alphabetic keyboard
|
||||||
return device.getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC;
|
return device.getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC;
|
||||||
}
|
}
|
||||||
@@ -703,6 +727,13 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK for https://issuetracker.google.com/issues/163120692
|
||||||
|
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
||||||
|
if (event.getDeviceId() == -1) {
|
||||||
|
return defaultContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return the existing context if it exists
|
// Return the existing context if it exists
|
||||||
InputDeviceContext context = inputDeviceContexts.get(event.getDeviceId());
|
InputDeviceContext context = inputDeviceContexts.get(event.getDeviceId());
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
@@ -864,7 +895,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
return KeyEvent.KEYCODE_BUTTON_MODE;
|
return KeyEvent.KEYCODE_BUTTON_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((context.vendorId == 0x057e && context.productId == 0x2009) || // Switch Pro controller
|
// This mapping was adding in Android 10, then changed based on
|
||||||
|
// kernel changes (adding hid-nintendo) in Android 11. If we're
|
||||||
|
// on anything newer than Pie, just use the built-in mapping.
|
||||||
|
if ((context.vendorId == 0x057e && context.productId == 0x2009 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) || // Switch Pro controller
|
||||||
(context.vendorId == 0x0f0d && context.productId == 0x00c1)) { // HORIPAD for Switch
|
(context.vendorId == 0x0f0d && context.productId == 0x00c1)) { // HORIPAD for Switch
|
||||||
switch (event.getScanCode()) {
|
switch (event.getScanCode()) {
|
||||||
case 0x130:
|
case 0x130:
|
||||||
|
|||||||
+13
-5
@@ -38,23 +38,31 @@ public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptu
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
|
// SOURCE_MOUSE_RELATIVE is how SOURCE_MOUSE appears when our view has pointer capture.
|
||||||
|
// SOURCE_TOUCHPAD will have relative axes populated iff our view has pointer capture.
|
||||||
|
// See https://developer.android.com/reference/android/view/View#requestPointerCapture()
|
||||||
|
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE ||
|
||||||
|
(event.getSource() == InputDevice.SOURCE_TOUCHPAD && targetView.hasPointerCapture());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getRelativeAxisX(MotionEvent event) {
|
public float getRelativeAxisX(MotionEvent event) {
|
||||||
float x = event.getX();
|
int axis = (event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) ?
|
||||||
|
MotionEvent.AXIS_X : MotionEvent.AXIS_RELATIVE_X;
|
||||||
|
float x = event.getAxisValue(axis);
|
||||||
for (int i = 0; i < event.getHistorySize(); i++) {
|
for (int i = 0; i < event.getHistorySize(); i++) {
|
||||||
x += event.getHistoricalX(i);
|
x += event.getHistoricalAxisValue(axis, i);
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getRelativeAxisY(MotionEvent event) {
|
public float getRelativeAxisY(MotionEvent event) {
|
||||||
float y = event.getY();
|
int axis = (event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) ?
|
||||||
|
MotionEvent.AXIS_Y : MotionEvent.AXIS_RELATIVE_Y;
|
||||||
|
float y = event.getAxisValue(axis);
|
||||||
for (int i = 0; i < event.getHistorySize(); i++) {
|
for (int i = 0; i < event.getHistorySize(); i++) {
|
||||||
y += event.getHistoricalY(i);
|
y += event.getHistoricalAxisValue(axis, i);
|
||||||
}
|
}
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -638,7 +638,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public int submitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int decodeUnitType,
|
public int submitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int decodeUnitType,
|
||||||
int frameNumber, long receiveTimeMs) {
|
int frameNumber, long receiveTimeMs, long enqueueTimeMs) {
|
||||||
if (stopping) {
|
if (stopping) {
|
||||||
// Don't bother if we're stopping
|
// Don't bother if we're stopping
|
||||||
return MoonBridge.DR_OK;
|
return MoonBridge.DR_OK;
|
||||||
@@ -699,11 +699,13 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
int inputBufferIndex;
|
int inputBufferIndex;
|
||||||
ByteBuffer buf;
|
ByteBuffer buf;
|
||||||
|
|
||||||
long timestampUs = System.nanoTime() / 1000;
|
long timestampUs = enqueueTimeMs * 1000;
|
||||||
|
|
||||||
if (!FRAME_RENDER_TIME_ONLY) {
|
if (!FRAME_RENDER_TIME_ONLY) {
|
||||||
// Count time from first packet received to decode start
|
// Count time from first packet received to enqueue time as receive time
|
||||||
activeWindowVideoStats.totalTimeMs += (timestampUs / 1000) - receiveTimeMs;
|
// We will count DU queue time as part of decoding, because it is directly
|
||||||
|
// caused by a slow decoder.
|
||||||
|
activeWindowVideoStats.totalTimeMs += enqueueTimeMs - receiveTimeMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timestampUs <= lastTimestampUs) {
|
if (timestampUs <= lastTimestampUs) {
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ public class MediaCodecHelper {
|
|||||||
private static final List<String> blacklisted59FpsDecoderPrefixes;
|
private static final List<String> blacklisted59FpsDecoderPrefixes;
|
||||||
private static final List<String> qualcommDecoderPrefixes;
|
private static final List<String> qualcommDecoderPrefixes;
|
||||||
|
|
||||||
|
public static final boolean IS_EMULATOR = Build.HARDWARE.equals("ranchu") || Build.HARDWARE.equals("cheets");
|
||||||
|
|
||||||
private static boolean isLowEndSnapdragon = false;
|
private static boolean isLowEndSnapdragon = false;
|
||||||
private static boolean isAdreno620 = false;
|
private static boolean isAdreno620 = false;
|
||||||
private static boolean initialized = false;
|
private static boolean initialized = false;
|
||||||
@@ -78,7 +80,7 @@ public class MediaCodecHelper {
|
|||||||
|
|
||||||
// Blacklist software decoders that don't support H264 high profile,
|
// Blacklist software decoders that don't support H264 high profile,
|
||||||
// but exclude the official AOSP and CrOS emulator from this restriction.
|
// but exclude the official AOSP and CrOS emulator from this restriction.
|
||||||
if (!Build.HARDWARE.equals("ranchu") && !Build.HARDWARE.equals("cheets")) {
|
if (!IS_EMULATOR) {
|
||||||
blacklistedDecoderPrefixes.add("omx.google");
|
blacklistedDecoderPrefixes.add("omx.google");
|
||||||
blacklistedDecoderPrefixes.add("AVCDecoder");
|
blacklistedDecoderPrefixes.add("AVCDecoder");
|
||||||
}
|
}
|
||||||
@@ -586,7 +588,7 @@ public class MediaCodecHelper {
|
|||||||
private static boolean isCodecBlacklisted(MediaCodecInfo codecInfo) {
|
private static boolean isCodecBlacklisted(MediaCodecInfo codecInfo) {
|
||||||
// Use the new isSoftwareOnly() function on Android Q
|
// Use the new isSoftwareOnly() function on Android Q
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
if (codecInfo.isSoftwareOnly()) {
|
if (!IS_EMULATOR && codecInfo.isSoftwareOnly()) {
|
||||||
LimeLog.info("Skipping software-only decoder: "+codecInfo.getName());
|
LimeLog.info("Skipping software-only decoder: "+codecInfo.getName());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public abstract class VideoDecoderRenderer {
|
|||||||
// This is called once for each frame-start NALU. This means it will be called several times
|
// This is called once for each frame-start NALU. This means it will be called several times
|
||||||
// for an IDR frame which contains several parameter sets and the I-frame data.
|
// for an IDR frame which contains several parameter sets and the I-frame data.
|
||||||
public abstract int submitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int decodeUnitType,
|
public abstract int submitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int decodeUnitType,
|
||||||
int frameNumber, long receiveTimeMs);
|
int frameNumber, long receiveTimeMs, long enqueueTimeMs);
|
||||||
|
|
||||||
public abstract void cleanup();
|
public abstract void cleanup();
|
||||||
|
|
||||||
|
|||||||
@@ -149,11 +149,11 @@ public class MoonBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int bridgeDrSubmitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength,
|
public static int bridgeDrSubmitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength,
|
||||||
int decodeUnitType,
|
int decodeUnitType, int frameNumber,
|
||||||
int frameNumber, long receiveTimeMs) {
|
long receiveTimeMs, long enqueueTimeMs) {
|
||||||
if (videoRenderer != null) {
|
if (videoRenderer != null) {
|
||||||
return videoRenderer.submitDecodeUnit(decodeUnitData, decodeUnitLength,
|
return videoRenderer.submitDecodeUnit(decodeUnitData, decodeUnitLength,
|
||||||
decodeUnitType, frameNumber, receiveTimeMs);
|
decodeUnitType, frameNumber, receiveTimeMs, enqueueTimeMs);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return DR_OK;
|
return DR_OK;
|
||||||
|
|||||||
@@ -109,6 +109,30 @@ public class PreferenceConfiguration {
|
|||||||
public boolean touchscreenTrackpad;
|
public boolean touchscreenTrackpad;
|
||||||
public MoonBridge.AudioConfiguration audioConfiguration;
|
public MoonBridge.AudioConfiguration audioConfiguration;
|
||||||
|
|
||||||
|
public static boolean isNativeResolution(int width, int height) {
|
||||||
|
// It's not a native resolution if it matches an existing resolution option
|
||||||
|
if (width == 640 && height == 360) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (width == 854 && height == 480) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (width == 1280 && height == 720) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (width == 1920 && height == 1080) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (width == 2560 && height == 1440) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (width == 3840 && height == 2160) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static String convertFromLegacyResolutionString(String resString) {
|
private static String convertFromLegacyResolutionString(String resString) {
|
||||||
if (resString.equalsIgnoreCase("360p")) {
|
if (resString.equalsIgnoreCase("360p")) {
|
||||||
return RES_360P;
|
return RES_360P;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class StreamSettings extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class SettingsFragment extends PreferenceFragment {
|
public static class SettingsFragment extends PreferenceFragment {
|
||||||
private boolean nativeResolutionOptionPresent = false;
|
private int nativeResolutionStartIndex = Integer.MAX_VALUE;
|
||||||
|
|
||||||
private void setValue(String preferenceKey, String value) {
|
private void setValue(String preferenceKey, String value) {
|
||||||
ListPreference pref = (ListPreference) findPreference(preferenceKey);
|
ListPreference pref = (ListPreference) findPreference(preferenceKey);
|
||||||
@@ -76,36 +76,35 @@ public class StreamSettings extends Activity {
|
|||||||
pref.setValue(value);
|
pref.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateNativeResolutionEntry(int nativeWidth, int nativeHeight) {
|
private void addNativeResolutionEntry(int nativeWidth, int nativeHeight) {
|
||||||
ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING);
|
ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING);
|
||||||
|
|
||||||
String nameSuffix = " ("+nativeWidth+"x"+nativeHeight+")";
|
String newName = getResources().getString(R.string.resolution_prefix_native) + " ("+nativeWidth+"x"+nativeHeight+")";
|
||||||
String newValue = nativeWidth+"x"+nativeHeight;
|
String newValue = nativeWidth+"x"+nativeHeight;
|
||||||
|
|
||||||
CharSequence[] entries = pref.getEntries();
|
|
||||||
CharSequence[] values = pref.getEntryValues();
|
CharSequence[] values = pref.getEntryValues();
|
||||||
|
|
||||||
// Check if the native resolution is already present
|
// Check if the native resolution is already present
|
||||||
for (CharSequence value : values) {
|
for (CharSequence value : values) {
|
||||||
if (newValue.equals(value.toString())) {
|
if (newValue.equals(value.toString())) {
|
||||||
// It is present in the default list, so remove the native option
|
// It is present in the default list, so don't add it again
|
||||||
nativeResolutionOptionPresent = false;
|
|
||||||
|
|
||||||
pref.setEntries(Arrays.copyOf(entries, entries.length - 1));
|
|
||||||
pref.setEntryValues(Arrays.copyOf(values, values.length - 1));
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the name suffix to the native option
|
CharSequence[] newEntries = Arrays.copyOf(pref.getEntries(), pref.getEntries().length + 1);
|
||||||
entries[entries.length - 1] = entries[entries.length - 1].toString() + nameSuffix;
|
CharSequence[] newValues = Arrays.copyOf(values, values.length + 1);
|
||||||
values[values.length - 1] = newValue;
|
|
||||||
|
|
||||||
pref.setEntries(entries);
|
// Add the new native option
|
||||||
pref.setEntryValues(values);
|
newEntries[newEntries.length - 1] = newName;
|
||||||
|
newValues[newValues.length - 1] = newValue;
|
||||||
|
|
||||||
nativeResolutionOptionPresent = true;
|
pref.setEntries(newEntries);
|
||||||
|
pref.setEntryValues(newValues);
|
||||||
|
|
||||||
|
if (newValues.length - 1 < nativeResolutionStartIndex) {
|
||||||
|
nativeResolutionStartIndex = newValues.length - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeValue(String preferenceKey, String value, Runnable onMatched) {
|
private void removeValue(String preferenceKey, String value, Runnable onMatched) {
|
||||||
@@ -226,7 +225,6 @@ public class StreamSettings extends Activity {
|
|||||||
// HEVC Decoder: OMX.amlogic.hevc.decoder.awesome
|
// HEVC Decoder: OMX.amlogic.hevc.decoder.awesome
|
||||||
// AVC supported width range: 64 - 384
|
// AVC supported width range: 64 - 384
|
||||||
// HEVC supported width range: 64 - 544
|
// HEVC supported width range: 64 - 544
|
||||||
int nativeWidth = 0, nativeHeight = 0;
|
|
||||||
for (Display.Mode candidate : display.getSupportedModes()) {
|
for (Display.Mode candidate : display.getSupportedModes()) {
|
||||||
// Some devices report their dimensions in the portrait orientation
|
// Some devices report their dimensions in the portrait orientation
|
||||||
// where height > width. Normalize these to the conventional width > height
|
// where height > width. Normalize these to the conventional width > height
|
||||||
@@ -235,12 +233,7 @@ public class StreamSettings extends Activity {
|
|||||||
int width = Math.max(candidate.getPhysicalWidth(), candidate.getPhysicalHeight());
|
int width = Math.max(candidate.getPhysicalWidth(), candidate.getPhysicalHeight());
|
||||||
int height = Math.min(candidate.getPhysicalWidth(), candidate.getPhysicalHeight());
|
int height = Math.min(candidate.getPhysicalWidth(), candidate.getPhysicalHeight());
|
||||||
|
|
||||||
if (width > nativeWidth) {
|
addNativeResolutionEntry(width, height);
|
||||||
nativeWidth = width;
|
|
||||||
}
|
|
||||||
if (height > nativeHeight) {
|
|
||||||
nativeHeight = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((width >= 3840 || height >= 2160) && maxSupportedResW < 3840) {
|
if ((width >= 3840 || height >= 2160) && maxSupportedResW < 3840) {
|
||||||
maxSupportedResW = 3840;
|
maxSupportedResW = 3840;
|
||||||
@@ -257,8 +250,6 @@ public class StreamSettings extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNativeResolutionEntry(nativeWidth, nativeHeight);
|
|
||||||
|
|
||||||
// This must be called to do runtime initialization before calling functions that evaluate
|
// This must be called to do runtime initialization before calling functions that evaluate
|
||||||
// decoder lists.
|
// decoder lists.
|
||||||
MediaCodecHelper.initialize(getContext(), GlPreferences.readPreferences(getContext()).glRenderer);
|
MediaCodecHelper.initialize(getContext(), GlPreferences.readPreferences(getContext()).glRenderer);
|
||||||
@@ -347,7 +338,7 @@ public class StreamSettings extends Activity {
|
|||||||
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
||||||
int width = Math.max(display.getWidth(), display.getHeight());
|
int width = Math.max(display.getWidth(), display.getHeight());
|
||||||
int height = Math.min(display.getWidth(), display.getHeight());
|
int height = Math.min(display.getWidth(), display.getHeight());
|
||||||
updateNativeResolutionEntry(width, height);
|
addNativeResolutionEntry(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PreferenceConfiguration.readPreferences(this.getActivity()).unlockFps) {
|
if (!PreferenceConfiguration.readPreferences(this.getActivity()).unlockFps) {
|
||||||
@@ -458,25 +449,23 @@ public class StreamSettings extends Activity {
|
|||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
||||||
String valueStr = (String) newValue;
|
String valueStr = (String) newValue;
|
||||||
|
|
||||||
if (nativeResolutionOptionPresent) {
|
// Detect if this value is the native resolution option
|
||||||
// Detect if this value is the native resolution option
|
CharSequence[] values = ((ListPreference)preference).getEntryValues();
|
||||||
CharSequence[] values = ((ListPreference)preference).getEntryValues();
|
boolean isNativeRes = true;
|
||||||
boolean isNativeRes = true;
|
for (int i = 0; i < values.length; i++) {
|
||||||
for (int i = 0; i < values.length; i++) {
|
// Look for a match prior to the start of the native resolution entries
|
||||||
// If get a match prior to the end, it's not native res
|
if (valueStr.equals(values[i].toString()) && i < nativeResolutionStartIndex) {
|
||||||
if (valueStr.equals(values[i].toString()) && i < values.length - 1) {
|
isNativeRes = false;
|
||||||
isNativeRes = false;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If this is native resolution, show the warning dialog
|
// If this is native resolution, show the warning dialog
|
||||||
if (isNativeRes) {
|
if (isNativeRes) {
|
||||||
Dialog.displayDialog(getActivity(),
|
Dialog.displayDialog(getActivity(),
|
||||||
getResources().getString(R.string.title_native_res_dialog),
|
getResources().getString(R.string.title_native_res_dialog),
|
||||||
getResources().getString(R.string.text_native_res_dialog),
|
getResources().getString(R.string.text_native_res_dialog),
|
||||||
false);
|
false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the new bitrate value
|
// Write the new bitrate value
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ Java_com_limelight_nvstream_jni_MoonBridge_init(JNIEnv *env, jclass clazz) {
|
|||||||
BridgeDrStartMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrStart", "()V");
|
BridgeDrStartMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrStart", "()V");
|
||||||
BridgeDrStopMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrStop", "()V");
|
BridgeDrStopMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrStop", "()V");
|
||||||
BridgeDrCleanupMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrCleanup", "()V");
|
BridgeDrCleanupMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrCleanup", "()V");
|
||||||
BridgeDrSubmitDecodeUnitMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrSubmitDecodeUnit", "([BIIIJ)I");
|
BridgeDrSubmitDecodeUnitMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrSubmitDecodeUnit", "([BIIIJJ)I");
|
||||||
BridgeArInitMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArInit", "(III)I");
|
BridgeArInitMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArInit", "(III)I");
|
||||||
BridgeArStartMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArStart", "()V");
|
BridgeArStartMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArStart", "()V");
|
||||||
BridgeArStopMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArStop", "()V");
|
BridgeArStopMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArStop", "()V");
|
||||||
@@ -157,7 +157,8 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
|
|
||||||
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
||||||
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
|
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
|
||||||
decodeUnit->frameNumber, (jlong)decodeUnit->receiveTimeMs);
|
decodeUnit->frameNumber, (jlong)decodeUnit->receiveTimeMs,
|
||||||
|
(jlong)decodeUnit->enqueueTimeMs);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
// We will crash here
|
// We will crash here
|
||||||
(*JVM)->DetachCurrentThread(JVM);
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
@@ -178,7 +179,7 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
||||||
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
|
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
|
||||||
decodeUnit->frameNumber,
|
decodeUnit->frameNumber,
|
||||||
(jlong)decodeUnit->receiveTimeMs);
|
(jlong)decodeUnit->receiveTimeMs, (jlong)decodeUnit->enqueueTimeMs);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
// We will crash here
|
// We will crash here
|
||||||
(*JVM)->DetachCurrentThread(JVM);
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
|||||||
Submodule app/src/main/jni/moonlight-core/moonlight-common-c updated: 3aa2463856...021fe902d9.
@@ -7,8 +7,9 @@
|
|||||||
<item>1080p</item>
|
<item>1080p</item>
|
||||||
<item>1440p</item>
|
<item>1440p</item>
|
||||||
<item>4K</item>
|
<item>4K</item>
|
||||||
<item>Native</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Keep this in sync with PreferenceConfiguration.isNativeResolution()! -->
|
||||||
<string-array name="resolution_values" translatable="false">
|
<string-array name="resolution_values" translatable="false">
|
||||||
<item>640x360</item>
|
<item>640x360</item>
|
||||||
<item>854x480</item>
|
<item>854x480</item>
|
||||||
@@ -16,7 +17,6 @@
|
|||||||
<item>1920x1080</item>
|
<item>1920x1080</item>
|
||||||
<item>2560x1440</item>
|
<item>2560x1440</item>
|
||||||
<item>3840x2160</item>
|
<item>3840x2160</item>
|
||||||
<item>Native</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="fps_names">
|
<string-array name="fps_names">
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
<string name="help_loading_msg">Loading help page…</string>
|
<string name="help_loading_msg">Loading help page…</string>
|
||||||
|
|
||||||
<!-- PC view menu entries -->
|
<!-- PC view menu entries -->
|
||||||
<string name="pcview_menu_header_online">PC Status: Online</string>
|
<string name="pcview_menu_header_online">Online</string>
|
||||||
<string name="pcview_menu_header_offline">PC Status: Offline</string>
|
<string name="pcview_menu_header_offline">Offline</string>
|
||||||
<string name="pcview_menu_header_unknown">PC Status: Refreshing</string>
|
<string name="pcview_menu_header_unknown">Refreshing</string>
|
||||||
<string name="pcview_menu_app_list">View All Apps</string>
|
<string name="pcview_menu_app_list">View All Apps</string>
|
||||||
<string name="pcview_menu_pair_pc">Pair with PC</string>
|
<string name="pcview_menu_pair_pc">Pair with PC</string>
|
||||||
<string name="pcview_menu_unpair_pc">Unpair</string>
|
<string name="pcview_menu_unpair_pc">Unpair</string>
|
||||||
@@ -147,6 +147,7 @@
|
|||||||
<string name="summary_seekbar_bitrate">Increase for better image quality. Decrease to improve performance on slower connections.</string>
|
<string name="summary_seekbar_bitrate">Increase for better image quality. Decrease to improve performance on slower connections.</string>
|
||||||
<string name="suffix_seekbar_bitrate_mbps">Mbps</string>
|
<string name="suffix_seekbar_bitrate_mbps">Mbps</string>
|
||||||
<string name="title_checkbox_stretch_video">Stretch video to full-screen</string>
|
<string name="title_checkbox_stretch_video">Stretch video to full-screen</string>
|
||||||
|
<string name="resolution_prefix_native">Native</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Audio Settings</string>
|
<string name="category_audio_settings">Audio Settings</string>
|
||||||
<string name="title_audio_config_list">Surround sound configuration</string>
|
<string name="title_audio_config_list">Surround sound configuration</string>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
version: 0.0.0.{build}
|
||||||
|
|
||||||
|
clone_depth: 1
|
||||||
|
|
||||||
|
image: Visual Studio 2019
|
||||||
|
|
||||||
|
before_build:
|
||||||
|
- 'git submodule update --init --recursive'
|
||||||
|
- 'mklink /D C:\android-sdk "C:\Program Files (x86)\Android\android-sdk"'
|
||||||
|
- 'set ANDROID_HOME=C:\android-sdk'
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- gradlew.bat build connectedCheck
|
||||||
|
|
||||||
|
after_build:
|
||||||
|
- 'appveyor PushArtifact app\build\reports\lint-results.html'
|
||||||
|
|
||||||
|
deploy: off
|
||||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
- Added support for multiple native resolution options
|
||||||
|
- Improve accuracy of decoder latency reports for very slow decoders
|
||||||
|
- Minor UI improvements
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
- Add a workaround for the Android 11 gamepad bug when Accessibility features are enabled
|
||||||
|
- See https://issuetracker.google.com/issues/163120692 for more details
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
- Fixed mapping of Nintendo Switch Pro controller on Android 11
|
||||||
|
- Fixed handling of multiple input devices with different modifier states
|
||||||
Reference in new issue
Block a user