Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f555d3dae0 | |||
| 70f1a2cacb | |||
| 7f15aaa2e5 | |||
| e5726205c4 | |||
| 07fabc0663 | |||
| 800f97ae85 | |||
| 3ee5b284e1 | |||
| c0389f0da9 | |||
| a7a4d7ded5 | |||
| 87cd974b79 | |||
| 7faaac31ff | |||
| 7386eb2a78 | |||
| 49a1524f4f | |||
| c957b8b06b | |||
| a3a6e14d80 | |||
| 7231f5468b | |||
| 4dfb0d7220 | |||
| 2f4f53b048 | |||
| b6e8389544 | |||
| d113878613 | |||
| f7ed7e06db | |||
| 977a1d4a3c | |||
| eefc08db47 | |||
| ab2b1663d3 | |||
| 04b8a718e3 | |||
| 37cf260ba6 | |||
| 8f91fe4cd1 | |||
| 9246ad412f | |||
| 1ccbbdd4fb | |||
| 16cf37994d | |||
| 01e84624c2 | |||
| 939cd7cf70 | |||
| 4b11603035 | |||
| ca18b6b052 | |||
| 3d0d19e561 | |||
| ae463a8735 | |||
| 7e797829ae | |||
| 431ed6bc5d | |||
| e9bb711c42 | |||
| 623bc5c156 | |||
| cfefef4619 | |||
| 4a9a881c1f | |||
| 13a06d585c | |||
| 1c8ad64da0 | |||
| 1d8925de57 | |||
| 0eb7e779b8 | |||
| a4b86eefe2 | |||
| 902a58bc70 | |||
| a34a44f29a | |||
| 454fe80172 | |||
| 81b6a8a311 | |||
| 3011a5bad7 | |||
| dcb7be3acd | |||
| 68a6b510b1 | |||
| dca3e89303 | |||
| bae6fef588 | |||
| 37f65e43a5 | |||
| 8c910101c7 | |||
| 112d9c41eb | |||
| c91d1097f6 | |||
| 105c2c9eef | |||
| b754d2de28 | |||
| f6425c7ec6 | |||
| e690c9b8c8 | |||
| f87cbac77c | |||
| 150bd313cf | |||
| bc90cb894c | |||
| c51a75a681 | |||
| 68aa9bd12d | |||
| 1fb6bf4d70 | |||
| b4df3658f1 | |||
| 4efb4d9b24 | |||
| d61e893731 | |||
| 951e44728e | |||
| 8dcdf73222 | |||
| 5e4f37532c | |||
| f3f5ca74a3 | |||
| fdd4c0bbe1 |
@@ -1,4 +1,4 @@
|
||||
issuesOpened: >
|
||||
If this is a question about Moonlight or you need help troubleshooting a streaming problem, please use the help channels on our [Discord server](https://discord.gg/MySTSdq) instead of GitHub issues. There are many more people available on Discord to help you and answer your questions.<br /><br />
|
||||
If this is a question about Moonlight or you need help troubleshooting a streaming problem, please use the help channels on our [Discord server](https://moonlight-stream.org/discord) instead of GitHub issues. There are many more people available on Discord to help you and answer your questions.<br /><br />
|
||||
This issue tracker should only be used for specific bugs or feature requests.<br /><br />
|
||||
Thank you, and happy streaming!
|
||||
|
||||
+5
-2
@@ -8,8 +8,11 @@ android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-29.0.1
|
||||
- build-tools-29.0.3
|
||||
- android-29
|
||||
|
||||
before_install:
|
||||
- sdkmanager --list
|
||||
|
||||
install:
|
||||
- yes | sdkmanager "ndk-bundle"
|
||||
- yes | sdkmanager "ndk;20.0.5594570"
|
||||
+6
-6
@@ -7,8 +7,8 @@ android {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
|
||||
versionName "8.8"
|
||||
versionCode = 208
|
||||
versionName "9.0"
|
||||
versionCode = 219
|
||||
}
|
||||
|
||||
flavorDimensions "root"
|
||||
@@ -114,10 +114,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.62'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.62'
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.64'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.64'
|
||||
implementation 'org.jcodec:jcodec:0.2.3'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.12.3'
|
||||
implementation 'com.squareup.okio:okio:1.17.4'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.12.10'
|
||||
implementation 'com.squareup.okio:okio:1.17.5'
|
||||
implementation 'org.jmdns:jmdns:3.5.5'
|
||||
}
|
||||
|
||||
@@ -517,6 +517,12 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
if (!foundExistingApp) {
|
||||
// This app must be new
|
||||
appGridAdapter.addApp(new AppObject(app));
|
||||
|
||||
// We could have a leftover shortcut from last time this PC was paired
|
||||
// or if this app was removed then added again. Enable those shortcuts
|
||||
// again if present.
|
||||
shortcutHelper.enableAppShortcut(computer, app);
|
||||
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
@@ -73,6 +74,7 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
@@ -398,6 +400,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Hopefully, we can get rid of this once someone comes up with a better way
|
||||
// to track the state of the pipeline and time frames.
|
||||
int roundedRefreshRate = Math.round(displayRefreshRate);
|
||||
int chosenFrameRate = prefConfig.fps;
|
||||
if (!prefConfig.disableFrameDrop || prefConfig.unlockFps) {
|
||||
if (Build.DEVICE.equals("coral") || Build.DEVICE.equals("flame")) {
|
||||
// HACK: Pixel 4 (XL) ignores the preferred display mode and lowers refresh rate,
|
||||
@@ -423,8 +426,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Use the old rendering strategy on these broken devices
|
||||
decoderRenderer.enableLegacyFrameDropRendering();
|
||||
} else {
|
||||
prefConfig.fps = roundedRefreshRate - 1;
|
||||
LimeLog.info("Adjusting FPS target for screen to " + prefConfig.fps);
|
||||
chosenFrameRate = roundedRefreshRate - 1;
|
||||
LimeLog.info("Adjusting FPS target for screen to " + chosenFrameRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,7 +439,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
StreamConfiguration config = new StreamConfiguration.Builder()
|
||||
.setResolution(prefConfig.width, prefConfig.height)
|
||||
.setRefreshRate(prefConfig.fps)
|
||||
.setLaunchRefreshRate(prefConfig.fps)
|
||||
.setRefreshRate(chosenFrameRate)
|
||||
.setApp(new NvApp(appName != null ? appName : "app", appId, willStreamHdr))
|
||||
.setBitrate(prefConfig.bitrate)
|
||||
.setEnableSops(prefConfig.enableSops)
|
||||
@@ -450,9 +454,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
.setEnableHdr(willStreamHdr)
|
||||
.setAttachedGamepadMask(gamepadMask)
|
||||
.setClientRefreshRateX100((int)(displayRefreshRate * 100))
|
||||
.setAudioConfiguration(prefConfig.enable51Surround ?
|
||||
MoonBridge.AUDIO_CONFIGURATION_51_SURROUND :
|
||||
MoonBridge.AUDIO_CONFIGURATION_STEREO)
|
||||
.setAudioConfiguration(prefConfig.audioConfiguration)
|
||||
.build();
|
||||
|
||||
// Initialize the connection
|
||||
@@ -592,9 +594,33 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Remove when Android R SDK is finalized
|
||||
private static void setPreferMinimalPostProcessingWithReflection(WindowManager.LayoutParams windowLayoutParams, boolean isPreferred) {
|
||||
// Build.VERSION.PREVIEW_SDK_INT was added in M
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && Build.VERSION.PREVIEW_SDK_INT == 0) {
|
||||
// Don't attempt this reflection unless on Android R Developer Preview
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Field field = windowLayoutParams.getClass().getDeclaredField("preferMinimalPostProcessing");
|
||||
field.set(windowLayoutParams, isPreferred);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private float prepareDisplayForRendering() {
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
||||
float displayRefreshRate;
|
||||
|
||||
// On M, we can explicitly set the optimal display mode
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
@@ -638,6 +664,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
|
||||
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
|
||||
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
|
||||
displayRefreshRate = bestMode.getRefreshRate();
|
||||
}
|
||||
// On L, we can at least tell the OS that we want a refresh rate
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
@@ -658,12 +685,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
LimeLog.info("Selected refresh rate: "+bestRefreshRate);
|
||||
windowLayoutParams.preferredRefreshRate = bestRefreshRate;
|
||||
displayRefreshRate = bestRefreshRate;
|
||||
}
|
||||
else {
|
||||
// Otherwise, the active display refresh rate is just
|
||||
// whatever is currently in use.
|
||||
displayRefreshRate = display.getRefreshRate();
|
||||
}
|
||||
|
||||
// Enable HDMI ALLM (game mode) on Android R
|
||||
setPreferMinimalPostProcessingWithReflection(windowLayoutParams, true);
|
||||
|
||||
// Apply the display mode change
|
||||
getWindow().setAttributes(windowLayoutParams);
|
||||
|
||||
@@ -698,9 +730,18 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
||||
}
|
||||
|
||||
// Use the actual refresh rate of the display, since the preferred refresh rate or mode
|
||||
// may not actually be applied (ex: Pixel 4 with Smooth Display disabled).
|
||||
return getWindowManager().getDefaultDisplay().getRefreshRate();
|
||||
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) ||
|
||||
getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
|
||||
// TVs may take a few moments to switch refresh rates, and we can probably assume
|
||||
// it will be eventually activated.
|
||||
// TODO: Improve this
|
||||
return displayRefreshRate;
|
||||
}
|
||||
else {
|
||||
// Use the actual refresh rate of the display, since the preferred refresh rate or mode
|
||||
// may not actually be applied (ex: Pixel 4 with Smooth Display disabled).
|
||||
return getWindowManager().getDefaultDisplay().getRefreshRate();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
@@ -959,11 +1000,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Handle a synthetic back button event that some Android OS versions
|
||||
// create as a result of a right-click. This event WILL repeat if
|
||||
// the right mouse button is held down, so we ignore those.
|
||||
if (!prefConfig.mouseNavButtons &&
|
||||
(event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||
if ((event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
||||
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||
|
||||
// Send the right mouse button event if mouse back and forward
|
||||
// are disabled. If they are enabled, handleMotionEvent() will take
|
||||
// care of this.
|
||||
if (!prefConfig.mouseNavButtons) {
|
||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||
}
|
||||
|
||||
// Always return true, otherwise the back press will be propagated
|
||||
// up to the parent and finish the activity.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -987,6 +1036,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Eat repeat down events
|
||||
if (event.getRepeatCount() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pass through keyboard input if we're not grabbing
|
||||
if (!grabbedInput) {
|
||||
return false;
|
||||
@@ -1017,11 +1071,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
// Handle a synthetic back button event that some Android OS versions
|
||||
// create as a result of a right-click.
|
||||
if (!prefConfig.mouseNavButtons &&
|
||||
(event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||
if ((event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
||||
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||
|
||||
// Send the right mouse button event if mouse back and forward
|
||||
// are disabled. If they are enabled, handleMotionEvent() will take
|
||||
// care of this.
|
||||
if (!prefConfig.mouseNavButtons) {
|
||||
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||
}
|
||||
|
||||
// Always return true, otherwise the back press will be propagated
|
||||
// up to the parent and finish the activity.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1103,7 +1165,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
// Ignore mouse input if we're not capturing from our input source
|
||||
if (!inputCaptureProvider.isCapturingActive()) {
|
||||
return false;
|
||||
// We return true here because otherwise the events may end up causing
|
||||
// Android to synthesize d-pad events.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
||||
@@ -1194,8 +1258,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
else
|
||||
{
|
||||
if (virtualController != null &&
|
||||
virtualController.getControllerMode() == VirtualController.ControllerMode.Configuration) {
|
||||
// Ignore presses when the virtual controller is in configuration mode
|
||||
(virtualController.getControllerMode() == VirtualController.ControllerMode.MoveButtons ||
|
||||
virtualController.getControllerMode() == VirtualController.ControllerMode.ResizeButtons)) {
|
||||
// Ignore presses when the virtual controller is being configured
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1372,7 +1437,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stageFailed(final String stage, final long errorCode) {
|
||||
public void stageFailed(final String stage, final int errorCode) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -1387,18 +1452,18 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
// If video initialization failed and the surface is still valid, display extra information for the user
|
||||
if (stage.contains("video") && streamView.getHolder().getSurface().isValid()) {
|
||||
Toast.makeText(Game.this, "Video decoder failed to initialize. Your device may not support the selected resolution.", Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(Game.this, getResources().getText(R.string.video_decoder_init_failed), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.conn_error_msg) + " " + stage, true);
|
||||
getResources().getString(R.string.conn_error_msg) + " " + stage +" (error "+errorCode+")", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionTerminated(final long errorCode) {
|
||||
public void connectionTerminated(final int errorCode) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -64,25 +64,46 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setup(int audioConfiguration, int sampleRate, int samplesPerFrame) {
|
||||
public int setup(MoonBridge.AudioConfiguration audioConfiguration, int sampleRate, int samplesPerFrame) {
|
||||
int channelConfig;
|
||||
int bytesPerFrame;
|
||||
|
||||
switch (audioConfiguration)
|
||||
switch (audioConfiguration.channelCount)
|
||||
{
|
||||
case MoonBridge.AUDIO_CONFIGURATION_STEREO:
|
||||
case 2:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||
bytesPerFrame = 2 * samplesPerFrame * 2;
|
||||
break;
|
||||
case MoonBridge.AUDIO_CONFIGURATION_51_SURROUND:
|
||||
case 4:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
||||
break;
|
||||
case 6:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||
bytesPerFrame = 6 * samplesPerFrame * 2;
|
||||
break;
|
||||
case 8:
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// AudioFormat.CHANNEL_OUT_7POINT1_SURROUND isn't available until Android 6.0,
|
||||
// yet the CHANNEL_OUT_SIDE_LEFT and CHANNEL_OUT_SIDE_RIGHT constants were added
|
||||
// in 5.0, so just hardcode the constant so we can work on Lollipop.
|
||||
channelConfig = 0x000018fc; // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
|
||||
}
|
||||
else {
|
||||
// On KitKat and lower, creation of the AudioTrack will fail if we specify
|
||||
// CHANNEL_OUT_SIDE_LEFT or CHANNEL_OUT_SIDE_RIGHT. That leaves us with
|
||||
// the old CHANNEL_OUT_7POINT1 which uses left-of-center and right-of-center
|
||||
// speakers instead of side-left and side-right. This non-standard layout
|
||||
// is probably not what the user wants, but we don't really have a choice.
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LimeLog.severe("Decoder returned unhandled channel count");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LimeLog.info("Audio channel config: "+String.format("0x%X", channelConfig));
|
||||
|
||||
bytesPerFrame = audioConfiguration.channelCount * samplesPerFrame * 2;
|
||||
|
||||
// We're not supposed to request less than the minimum
|
||||
// buffer size for our buffer, but it appears that we can
|
||||
// do this on many devices and it lowers audio latency.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.limelight.binding.input;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
@@ -51,7 +52,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
private final SparseArray<UsbDeviceContext> usbDeviceContexts = new SparseArray<>();
|
||||
|
||||
private final NvConnection conn;
|
||||
private final Context activityContext;
|
||||
private final Activity activityContext;
|
||||
private final double stickDeadzone;
|
||||
private final InputDeviceContext defaultContext = new InputDeviceContext();
|
||||
private final GameGestures gestures;
|
||||
@@ -61,7 +62,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
private final PreferenceConfiguration prefConfig;
|
||||
private short currentControllers, initialControllers;
|
||||
|
||||
public ControllerHandler(Context activityContext, NvConnection conn, GameGestures gestures, PreferenceConfiguration prefConfig) {
|
||||
public ControllerHandler(Activity activityContext, NvConnection conn, GameGestures gestures, PreferenceConfiguration prefConfig) {
|
||||
this.activityContext = activityContext;
|
||||
this.conn = conn;
|
||||
this.gestures = gestures;
|
||||
@@ -108,6 +109,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
||||
defaultContext.controllerNumber = (short) 0;
|
||||
defaultContext.assignedControllerNumber = true;
|
||||
defaultContext.external = false;
|
||||
|
||||
// Some devices (GPD XD) have a back button which sends input events
|
||||
// with device ID == 0. This hits the default context which would normally
|
||||
@@ -146,6 +148,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
if (context != null) {
|
||||
LimeLog.info("Removed controller: "+context.name+" ("+deviceId+")");
|
||||
releaseControllerNumber(context);
|
||||
context.destroy();
|
||||
inputDeviceContexts.remove(deviceId);
|
||||
}
|
||||
}
|
||||
@@ -160,10 +163,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
public void stop() {
|
||||
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
||||
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
||||
deviceContext.destroy();
|
||||
}
|
||||
|
||||
if (deviceContext.vibrator != null) {
|
||||
deviceContext.vibrator.cancel();
|
||||
}
|
||||
for (int i = 0; i < usbDeviceContexts.size(); i++) {
|
||||
UsbDeviceContext deviceContext = usbDeviceContexts.valueAt(i);
|
||||
deviceContext.destroy();
|
||||
}
|
||||
|
||||
deviceVibrator.cancel();
|
||||
@@ -264,9 +269,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
InputDeviceContext devContext = (InputDeviceContext) context;
|
||||
|
||||
LimeLog.info(devContext.name+" ("+context.id+") needs a controller number assigned");
|
||||
if (devContext.name != null &&
|
||||
(devContext.name.contains("gpio-keys") || // This is the back button on Shield portable consoles
|
||||
devContext.name.contains("joy_key"))) { // These are the gamepad buttons on the Archos Gamepad 2
|
||||
if (!devContext.external) {
|
||||
LimeLog.info("Built-in buttons hardcoded as controller 0");
|
||||
context.controllerNumber = 0;
|
||||
}
|
||||
@@ -327,6 +330,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
context.id = device.getControllerId();
|
||||
context.device = device;
|
||||
context.external = true;
|
||||
|
||||
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
||||
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
||||
@@ -343,6 +347,17 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
return true;
|
||||
}
|
||||
|
||||
String deviceName = dev.getName();
|
||||
if (deviceName.contains("gpio") || // This is the back button on Shield portable consoles
|
||||
deviceName.contains("joy_key") || // These are the gamepad buttons on the Archos Gamepad 2
|
||||
deviceName.contains("keypad") || // These are gamepad buttons on the XPERIA Play
|
||||
deviceName.equalsIgnoreCase("NVIDIA Corporation NVIDIA Controller v01.01") || // Gamepad on Shield Portable
|
||||
deviceName.equalsIgnoreCase("NVIDIA Corporation NVIDIA Controller v01.02")) // Gamepad on Shield Portable (?)
|
||||
{
|
||||
LimeLog.info(dev.getName()+" is internal by hardcoded mapping");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
// Landroid/view/InputDevice;->isExternal()Z is officially public on Android Q
|
||||
return dev.isExternal();
|
||||
@@ -376,9 +391,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
// Classify this device as a remote by name if it has no joystick axes
|
||||
if (getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_X) == null &&
|
||||
getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Y) == null &&
|
||||
devName.toLowerCase().contains("remote")) {
|
||||
if (!hasJoystickAxes(dev) && devName.toLowerCase().contains("remote")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -401,11 +414,14 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
// Note that we are explicitly NOT excluding the current device we're examining here,
|
||||
// since the other gamepad buttons may be on our current device and that's fine.
|
||||
boolean[] keys = currentDev.hasKeys(KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BUTTON_A);
|
||||
if (keys[0]) {
|
||||
if (currentDev.hasKeys(KeyEvent.KEYCODE_BUTTON_SELECT)[0]) {
|
||||
foundInternalSelect = true;
|
||||
}
|
||||
if (keys[1]) {
|
||||
|
||||
// We don't check KEYCODE_BUTTON_A here, since the Shield Android TV has a
|
||||
// virtual mouse device that claims to have KEYCODE_BUTTON_A. Instead, we rely
|
||||
// on the SOURCE_GAMEPAD flag to be set on gamepad devices.
|
||||
if (hasGamepadButtons(currentDev)) {
|
||||
foundInternalGamepad = true;
|
||||
}
|
||||
}
|
||||
@@ -417,8 +433,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
// c) have an internal gamepad but no internal select button (NVIDIA SHIELD Portable)
|
||||
return !foundInternalGamepad || foundInternalSelect;
|
||||
}
|
||||
|
||||
return false;
|
||||
else {
|
||||
// For external devices, we want to pass through the back button if the device
|
||||
// has no gamepad axes or gamepad buttons.
|
||||
return !hasJoystickAxes(dev) && !hasGamepadButtons(dev);
|
||||
}
|
||||
}
|
||||
|
||||
private InputDeviceContext createInputDeviceContextForDevice(InputDevice dev) {
|
||||
@@ -434,6 +453,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
context.name = devName;
|
||||
context.id = dev.getId();
|
||||
context.external = isExternal(dev);
|
||||
|
||||
if (dev.getVibrator().hasVibrator()) {
|
||||
context.vibrator = dev.getVibrator();
|
||||
@@ -888,18 +908,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
}
|
||||
|
||||
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
||||
switch (event.getKeyCode()) {
|
||||
// These are duplicate dpad events for hat input
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (context.hatXAxis == -1 &&
|
||||
if (context.hatXAxis == -1 &&
|
||||
context.hatYAxis == -1 &&
|
||||
/* FIXME: There's no good way to know for sure if xpad is bound
|
||||
to this device, so we won't use the name to validate if these
|
||||
@@ -1030,17 +1039,21 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
context.inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG);
|
||||
if (hatX < -0.5) {
|
||||
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
||||
context.hatXAxisUsed = true;
|
||||
}
|
||||
else if (hatX > 0.5) {
|
||||
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
||||
context.hatXAxisUsed = true;
|
||||
}
|
||||
|
||||
context.inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG);
|
||||
if (hatY < -0.5) {
|
||||
context.inputMap |= ControllerPacket.UP_FLAG;
|
||||
context.hatYAxisUsed = true;
|
||||
}
|
||||
else if (hatY > 0.5) {
|
||||
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
||||
context.hatYAxisUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1255,15 +1268,31 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
context.inputMap &= ~ControllerPacket.BACK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
if (context.hatXAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap &= ~ControllerPacket.LEFT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
if (context.hatXAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap &= ~ControllerPacket.RIGHT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
if (context.hatYAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap &= ~ControllerPacket.UP_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
if (context.hatYAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap &= ~ControllerPacket.DOWN_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
@@ -1347,6 +1376,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
sendControllerInputPacket(context);
|
||||
|
||||
if (context.pendingExit && context.inputMap == 0) {
|
||||
// All buttons from the quit combo are lifted. Finish the activity now.
|
||||
activityContext.finish();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1377,15 +1412,31 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
context.inputMap |= ControllerPacket.BACK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
if (context.hatXAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
if (context.hatXAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
if (context.hatYAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap |= ControllerPacket.UP_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
if (context.hatYAxisUsed) {
|
||||
// Suppress this duplicate event if we have a hat
|
||||
return true;
|
||||
}
|
||||
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
@@ -1431,9 +1482,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start+Back+LB+RB is the quit combo
|
||||
if (context.inputMap == (ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG |
|
||||
ControllerPacket.LB_FLAG | ControllerPacket.RB_FLAG)) {
|
||||
// Wait for the combo to lift and then finish the activity
|
||||
context.pendingExit = true;
|
||||
}
|
||||
|
||||
// Start+LB acts like select for controllers with one button
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
|
||||
((context.inputMap & ControllerPacket.LB_FLAG) != 0 ||
|
||||
if (context.inputMap == (ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG) ||
|
||||
(context.inputMap == ControllerPacket.PLAY_FLAG &&
|
||||
SystemClock.uptimeMillis() - context.lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
||||
{
|
||||
context.inputMap &= ~(ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG);
|
||||
@@ -1443,10 +1501,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
// We detect select+start or start+RB as the special button combo
|
||||
if (((context.inputMap & ControllerPacket.RB_FLAG) != 0 ||
|
||||
(SystemClock.uptimeMillis() - context.lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS) ||
|
||||
(context.inputMap & ControllerPacket.BACK_FLAG) != 0) &&
|
||||
(context.inputMap & ControllerPacket.PLAY_FLAG) != 0)
|
||||
if (context.inputMap == (ControllerPacket.PLAY_FLAG | ControllerPacket.BACK_FLAG) ||
|
||||
context.inputMap == (ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG) ||
|
||||
(context.inputMap == ControllerPacket.PLAY_FLAG &&
|
||||
SystemClock.uptimeMillis() - context.lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
||||
{
|
||||
context.inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG);
|
||||
context.inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
||||
@@ -1454,7 +1512,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SPECIAL;
|
||||
}
|
||||
|
||||
|
||||
// We don't need to send repeat key down events, but the platform
|
||||
// sends us events that claim to be repeats but they're from different
|
||||
// devices, so we just send them all and deal with some duplicates.
|
||||
@@ -1525,6 +1582,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
if (context != null) {
|
||||
LimeLog.info("Removed controller: "+controller.getControllerId());
|
||||
releaseControllerNumber(context);
|
||||
context.destroy();
|
||||
usbDeviceContexts.remove(controller.getControllerId());
|
||||
}
|
||||
}
|
||||
@@ -1537,6 +1595,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
class GenericControllerContext {
|
||||
public int id;
|
||||
public boolean external;
|
||||
|
||||
public float leftStickDeadzoneRadius;
|
||||
public float rightStickDeadzoneRadius;
|
||||
@@ -1557,6 +1616,13 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
public boolean mouseEmulationActive;
|
||||
public Timer mouseEmulationTimer;
|
||||
public short mouseEmulationLastInputMap;
|
||||
|
||||
public void destroy() {
|
||||
if (mouseEmulationTimer != null) {
|
||||
mouseEmulationTimer.cancel();
|
||||
mouseEmulationTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InputDeviceContext extends GenericControllerContext {
|
||||
@@ -1576,6 +1642,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
public int hatXAxis = -1;
|
||||
public int hatYAxis = -1;
|
||||
public boolean hatXAxisUsed, hatYAxisUsed;
|
||||
|
||||
public boolean isNonStandardDualShock4;
|
||||
public boolean usesLinuxGamepadStandardFaceButtons;
|
||||
@@ -1585,6 +1652,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
public boolean modeIsSelect;
|
||||
public boolean ignoreBack;
|
||||
public boolean hasJoystickAxes;
|
||||
public boolean pendingExit;
|
||||
|
||||
public int emulatingButtonFlags = 0;
|
||||
|
||||
@@ -1597,9 +1665,25 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
public long lastRbUpTime = 0;
|
||||
|
||||
public long startDownTime = 0;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
|
||||
if (vibrator != null) {
|
||||
vibrator.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UsbDeviceContext extends GenericControllerContext {
|
||||
public AbstractController device;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
|
||||
// Nothing for now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
@@ -60,6 +61,7 @@ public class DigitalButton extends VirtualControllerElement {
|
||||
private TimerLongClickTimerTask longClickTimerTask = null;
|
||||
|
||||
private final Paint paint = new Paint();
|
||||
private final RectF rect = new RectF();
|
||||
|
||||
private int layer;
|
||||
private DigitalButton movingButton = null;
|
||||
@@ -144,14 +146,18 @@ public class DigitalButton extends VirtualControllerElement {
|
||||
// set transparent background
|
||||
canvas.drawColor(Color.TRANSPARENT);
|
||||
|
||||
paint.setTextSize(getPercent(getWidth(), 30));
|
||||
paint.setTextSize(getPercent(getWidth(), 25));
|
||||
paint.setTextAlign(Paint.Align.CENTER);
|
||||
paint.setStrokeWidth(getDefaultStrokeWidth());
|
||||
|
||||
paint.setColor(isPressed() ? pressedColor : getDefaultColor());
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawRect(paint.getStrokeWidth(), paint.getStrokeWidth(),
|
||||
getWidth() - paint.getStrokeWidth(), getHeight() - paint.getStrokeWidth(), paint);
|
||||
|
||||
rect.left = rect.top = paint.getStrokeWidth();
|
||||
rect.right = getWidth() - rect.left;
|
||||
rect.bottom = getHeight() - rect.top;
|
||||
|
||||
canvas.drawOval(rect, paint);
|
||||
|
||||
if (icon != -1) {
|
||||
Drawable d = getResources().getDrawable(icon);
|
||||
|
||||
+17
-5
@@ -34,7 +34,8 @@ public class VirtualController {
|
||||
|
||||
public enum ControllerMode {
|
||||
Active,
|
||||
Configuration
|
||||
MoveButtons,
|
||||
ResizeButtons
|
||||
}
|
||||
|
||||
private static final boolean _PRINT_DEBUG_INFORMATION = false;
|
||||
@@ -72,13 +73,16 @@ public class VirtualController {
|
||||
public void onClick(View v) {
|
||||
String message;
|
||||
|
||||
if (currentMode == ControllerMode.Configuration) {
|
||||
if (currentMode == ControllerMode.Active){
|
||||
currentMode = ControllerMode.MoveButtons;
|
||||
message = "Entering configuration mode (Move buttons)";
|
||||
} else if (currentMode == ControllerMode.MoveButtons) {
|
||||
currentMode = ControllerMode.ResizeButtons;
|
||||
message = "Entering configuration mode (Resize buttons)";
|
||||
} else {
|
||||
currentMode = ControllerMode.Active;
|
||||
VirtualControllerConfigurationLoader.saveProfile(VirtualController.this, context);
|
||||
message = "Exiting configuration mode";
|
||||
} else {
|
||||
currentMode = ControllerMode.Configuration;
|
||||
message = "Entering configuration mode";
|
||||
}
|
||||
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||
@@ -90,6 +94,7 @@ public class VirtualController {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
@@ -120,6 +125,13 @@ public class VirtualController {
|
||||
elements.clear();
|
||||
}
|
||||
|
||||
public void setOpacity(int opacity) {
|
||||
for (VirtualControllerElement element : elements) {
|
||||
element.setOpacity(opacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addElement(VirtualControllerElement element, int x, int y, int width, int height) {
|
||||
elements.add(element);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width, height);
|
||||
|
||||
+105
-67
@@ -24,6 +24,11 @@ public class VirtualControllerConfigurationLoader {
|
||||
return (int) (((float) total / (float) 100) * (float) percent);
|
||||
}
|
||||
|
||||
// The default controls are specified using a grid of 128*72 cells at 16:9
|
||||
private static int screenScale(int units, int height) {
|
||||
return (int) (((float) height / (float) 72) * (float) units);
|
||||
}
|
||||
|
||||
private static DigitalPad createDigitalPad(
|
||||
final VirtualController controller,
|
||||
final Context context) {
|
||||
@@ -145,149 +150,182 @@ public class VirtualControllerConfigurationLoader {
|
||||
return new RightAnalogStick(controller, context);
|
||||
}
|
||||
|
||||
private static final int BUTTON_BASE_X = 65;
|
||||
private static final int BUTTON_BASE_Y = 5;
|
||||
private static final int BUTTON_WIDTH = getPercent(30, 33);
|
||||
private static final int BUTTON_HEIGHT = getPercent(40, 33);
|
||||
|
||||
private static final int TRIGGER_L_BASE_X = 1;
|
||||
private static final int TRIGGER_R_BASE_X = 92;
|
||||
private static final int TRIGGER_DISTANCE = 23;
|
||||
private static final int TRIGGER_BASE_Y = 31;
|
||||
private static final int TRIGGER_WIDTH = 12;
|
||||
private static final int TRIGGER_HEIGHT = 9;
|
||||
|
||||
// Face buttons are defined based on the Y button (button number 9)
|
||||
private static final int BUTTON_BASE_X = 106;
|
||||
private static final int BUTTON_BASE_Y = 1;
|
||||
private static final int BUTTON_SIZE = 10;
|
||||
|
||||
private static final int DPAD_BASE_X = 4;
|
||||
private static final int DPAD_BASE_Y = 41;
|
||||
private static final int DPAD_SIZE = 30;
|
||||
|
||||
private static final int ANALOG_L_BASE_X = 4;
|
||||
private static final int ANALOG_L_BASE_Y = 1;
|
||||
private static final int ANALOG_R_BASE_X = 96;
|
||||
private static final int ANALOG_R_BASE_Y = 42;
|
||||
private static final int ANALOG_SIZE = 28;
|
||||
|
||||
private static final int L3_R3_BASE_Y = 60;
|
||||
|
||||
private static final int START_X = 83;
|
||||
private static final int BACK_X = 34;
|
||||
private static final int START_BACK_Y = 64;
|
||||
private static final int START_BACK_WIDTH = 12;
|
||||
private static final int START_BACK_HEIGHT = 7;
|
||||
|
||||
public static void createDefaultLayout(final VirtualController controller, final Context context) {
|
||||
|
||||
DisplayMetrics screen = context.getResources().getDisplayMetrics();
|
||||
PreferenceConfiguration config = PreferenceConfiguration.readPreferences(context);
|
||||
|
||||
// Displace controls on the right by this amount of pixels to account for different aspect ratios
|
||||
int rightDisplacement = screen.widthPixels - screen.heightPixels * 16 / 9;
|
||||
|
||||
int height = screen.heightPixels;
|
||||
|
||||
// NOTE: Some of these getPercent() expressions seem like they can be combined
|
||||
// into a single call. Due to floating point rounding, this isn't actually possible.
|
||||
|
||||
if (!config.onlyL3R3)
|
||||
{
|
||||
controller.addElement(createDigitalPad(controller, context),
|
||||
getPercent(5, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels),
|
||||
getPercent(30, screen.widthPixels),
|
||||
getPercent(40, screen.heightPixels)
|
||||
screenScale(DPAD_BASE_X, height),
|
||||
screenScale(DPAD_BASE_Y, height),
|
||||
screenScale(DPAD_SIZE, height),
|
||||
screenScale(DPAD_SIZE, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_A,
|
||||
ControllerPacket.A_FLAG, 0, 1, "A", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels) + getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels) + 2 * getPercent(BUTTON_HEIGHT, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(BUTTON_BASE_X, height) + rightDisplacement,
|
||||
screenScale(BUTTON_BASE_Y + 2 * BUTTON_SIZE, height),
|
||||
screenScale(BUTTON_SIZE, height),
|
||||
screenScale(BUTTON_SIZE, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_B,
|
||||
ControllerPacket.B_FLAG, 0, 1, "B", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels) + 2 * getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels) + getPercent(BUTTON_HEIGHT, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(BUTTON_BASE_X + BUTTON_SIZE, height) + rightDisplacement,
|
||||
screenScale(BUTTON_BASE_Y + BUTTON_SIZE, height),
|
||||
screenScale(BUTTON_SIZE, height),
|
||||
screenScale(BUTTON_SIZE, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_X,
|
||||
ControllerPacket.X_FLAG, 0, 1, "X", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels) + getPercent(BUTTON_HEIGHT, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(BUTTON_BASE_X - BUTTON_SIZE, height) + rightDisplacement,
|
||||
screenScale(BUTTON_BASE_Y + BUTTON_SIZE, height),
|
||||
screenScale(BUTTON_SIZE, height),
|
||||
screenScale(BUTTON_SIZE, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_Y,
|
||||
ControllerPacket.Y_FLAG, 0, 1, "Y", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels) + getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(BUTTON_BASE_X, height) + rightDisplacement,
|
||||
screenScale(BUTTON_BASE_Y, height),
|
||||
screenScale(BUTTON_SIZE, height),
|
||||
screenScale(BUTTON_SIZE, height)
|
||||
);
|
||||
|
||||
controller.addElement(createLeftTrigger(
|
||||
0, "LT", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
1, "LT", -1, controller, context),
|
||||
screenScale(TRIGGER_L_BASE_X, height),
|
||||
screenScale(TRIGGER_BASE_Y, height),
|
||||
screenScale(TRIGGER_WIDTH, height),
|
||||
screenScale(TRIGGER_HEIGHT, height)
|
||||
);
|
||||
|
||||
controller.addElement(createRightTrigger(
|
||||
0, "RT", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels) + 2 * getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
1, "RT", -1, controller, context),
|
||||
screenScale(TRIGGER_R_BASE_X + TRIGGER_DISTANCE, height) + rightDisplacement,
|
||||
screenScale(TRIGGER_BASE_Y, height),
|
||||
screenScale(TRIGGER_WIDTH, height),
|
||||
screenScale(TRIGGER_HEIGHT, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_LB,
|
||||
ControllerPacket.LB_FLAG, 0, 1, "LB", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels) + 2 * getPercent(BUTTON_HEIGHT, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(TRIGGER_L_BASE_X + TRIGGER_DISTANCE, height),
|
||||
screenScale(TRIGGER_BASE_Y, height),
|
||||
screenScale(TRIGGER_WIDTH, height),
|
||||
screenScale(TRIGGER_HEIGHT, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_RB,
|
||||
ControllerPacket.RB_FLAG, 0, 1, "RB", -1, controller, context),
|
||||
getPercent(BUTTON_BASE_X, screen.widthPixels) + 2 * getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_BASE_Y, screen.heightPixels) + 2 * getPercent(BUTTON_HEIGHT, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(TRIGGER_R_BASE_X, height) + rightDisplacement,
|
||||
screenScale(TRIGGER_BASE_Y, height),
|
||||
screenScale(TRIGGER_WIDTH, height),
|
||||
screenScale(TRIGGER_HEIGHT, height)
|
||||
);
|
||||
|
||||
controller.addElement(createLeftStick(controller, context),
|
||||
getPercent(5, screen.widthPixels),
|
||||
getPercent(50, screen.heightPixels),
|
||||
getPercent(40, screen.widthPixels),
|
||||
getPercent(50, screen.heightPixels)
|
||||
screenScale(ANALOG_L_BASE_X, height),
|
||||
screenScale(ANALOG_L_BASE_Y, height),
|
||||
screenScale(ANALOG_SIZE, height),
|
||||
screenScale(ANALOG_SIZE, height)
|
||||
);
|
||||
|
||||
controller.addElement(createRightStick(controller, context),
|
||||
getPercent(55, screen.widthPixels),
|
||||
getPercent(50, screen.heightPixels),
|
||||
getPercent(40, screen.widthPixels),
|
||||
getPercent(50, screen.heightPixels)
|
||||
screenScale(ANALOG_R_BASE_X, height) + rightDisplacement,
|
||||
screenScale(ANALOG_R_BASE_Y, height),
|
||||
screenScale(ANALOG_SIZE, height),
|
||||
screenScale(ANALOG_SIZE, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_BACK,
|
||||
ControllerPacket.BACK_FLAG, 0, 2, "BACK", -1, controller, context),
|
||||
getPercent(40, screen.widthPixels),
|
||||
getPercent(90, screen.heightPixels),
|
||||
getPercent(10, screen.widthPixels),
|
||||
getPercent(10, screen.heightPixels)
|
||||
screenScale(BACK_X, height),
|
||||
screenScale(START_BACK_Y, height),
|
||||
screenScale(START_BACK_WIDTH, height),
|
||||
screenScale(START_BACK_HEIGHT, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_START,
|
||||
ControllerPacket.PLAY_FLAG, 0, 3, "START", -1, controller, context),
|
||||
getPercent(40, screen.widthPixels) + getPercent(10, screen.widthPixels),
|
||||
getPercent(90, screen.heightPixels),
|
||||
getPercent(10, screen.widthPixels),
|
||||
getPercent(10, screen.heightPixels)
|
||||
screenScale(START_X, height) + rightDisplacement,
|
||||
screenScale(START_BACK_Y, height),
|
||||
screenScale(START_BACK_WIDTH, height),
|
||||
screenScale(START_BACK_HEIGHT, height)
|
||||
);
|
||||
}
|
||||
else {
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_LSB,
|
||||
ControllerPacket.LS_CLK_FLAG, 0, 1, "L3", -1, controller, context),
|
||||
getPercent(2, screen.widthPixels),
|
||||
getPercent(80, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(TRIGGER_L_BASE_X, height),
|
||||
screenScale(L3_R3_BASE_Y, height),
|
||||
screenScale(TRIGGER_WIDTH, height),
|
||||
screenScale(TRIGGER_HEIGHT, height)
|
||||
);
|
||||
|
||||
controller.addElement(createDigitalButton(
|
||||
VirtualControllerElement.EID_RSB,
|
||||
ControllerPacket.RS_CLK_FLAG, 0, 1, "R3", -1, controller, context),
|
||||
getPercent(89, screen.widthPixels),
|
||||
getPercent(80, screen.heightPixels),
|
||||
getPercent(BUTTON_WIDTH, screen.widthPixels),
|
||||
getPercent(BUTTON_HEIGHT, screen.heightPixels)
|
||||
screenScale(TRIGGER_R_BASE_X + TRIGGER_DISTANCE, height) + rightDisplacement,
|
||||
screenScale(L3_R3_BASE_Y, height),
|
||||
screenScale(TRIGGER_WIDTH, height),
|
||||
screenScale(TRIGGER_HEIGHT, height)
|
||||
);
|
||||
}
|
||||
|
||||
controller.setOpacity(config.oscOpacity);
|
||||
}
|
||||
|
||||
public static void saveProfile(final VirtualController controller,
|
||||
@@ -325,4 +363,4 @@ public class VirtualControllerConfigurationLoader {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-4
@@ -43,7 +43,8 @@ public abstract class VirtualControllerElement extends View {
|
||||
|
||||
private int normalColor = 0xF0888888;
|
||||
protected int pressedColor = 0xF00000FF;
|
||||
private int configNormalColor = 0xF0FF0000;
|
||||
private int configMoveColor = 0xF0FF0000;
|
||||
private int configResizeColor = 0xF0FF00FF;
|
||||
private int configSelectedColor = 0xF000FF00;
|
||||
|
||||
protected int startSize_x;
|
||||
@@ -156,8 +157,12 @@ public abstract class VirtualControllerElement extends View {
|
||||
}
|
||||
|
||||
protected int getDefaultColor() {
|
||||
return (virtualController.getControllerMode() == VirtualController.ControllerMode.Configuration) ?
|
||||
configNormalColor : normalColor;
|
||||
if (virtualController.getControllerMode() == VirtualController.ControllerMode.MoveButtons)
|
||||
return configMoveColor;
|
||||
else if (virtualController.getControllerMode() == VirtualController.ControllerMode.ResizeButtons)
|
||||
return configResizeColor;
|
||||
else
|
||||
return normalColor;
|
||||
}
|
||||
|
||||
protected int getDefaultStrokeWidth() {
|
||||
@@ -230,7 +235,10 @@ public abstract class VirtualControllerElement extends View {
|
||||
startSize_x = getWidth();
|
||||
startSize_y = getHeight();
|
||||
|
||||
actionEnableMove();
|
||||
if (virtualController.getControllerMode() == VirtualController.ControllerMode.MoveButtons)
|
||||
actionEnableMove();
|
||||
else if (virtualController.getControllerMode() == VirtualController.ControllerMode.ResizeButtons)
|
||||
actionEnableResize();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -287,6 +295,15 @@ public abstract class VirtualControllerElement extends View {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
public void setOpacity(int opacity) {
|
||||
int hexOpacity = opacity * 255 / 100;
|
||||
this.normalColor = (hexOpacity << 24) | (normalColor & 0x00FFFFFF);
|
||||
this.pressedColor = (hexOpacity << 24) | (pressedColor & 0x00FFFFFF);
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
protected final float getPercent(float value, float percent) {
|
||||
return value / 100 * percent;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
private Thread rendererThread;
|
||||
private boolean needsSpsBitstreamFixup, isExynos4;
|
||||
private boolean adaptivePlayback, directSubmit;
|
||||
private boolean lowLatency;
|
||||
private boolean constrainedHighProfile;
|
||||
private boolean refFrameInvalidationAvc, refFrameInvalidationHevc;
|
||||
private boolean refFrameInvalidationActive;
|
||||
@@ -60,6 +61,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
private boolean legacyFrameDropRendering = false;
|
||||
private PerfOverlayListener perfListener;
|
||||
|
||||
private MediaFormat inputFormat;
|
||||
private MediaFormat outputFormat;
|
||||
private MediaFormat configuredFormat;
|
||||
|
||||
private boolean needsBaselineSpsHack;
|
||||
private SeqParameterSet savedSps;
|
||||
|
||||
@@ -161,7 +166,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
// shared between AVC and HEVC decoders on the same device.
|
||||
if (avcDecoder != null) {
|
||||
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
||||
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(avcDecoder);
|
||||
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName(), prefs.height);
|
||||
refFrameInvalidationHevc = MediaCodecHelper.decoderSupportsRefFrameInvalidationHevc(avcDecoder.getName());
|
||||
|
||||
@@ -264,6 +268,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
}
|
||||
|
||||
refFrameInvalidationActive = refFrameInvalidationAvc;
|
||||
|
||||
lowLatency = MediaCodecHelper.decoderSupportsLowLatency(avcDecoder, mimeType);
|
||||
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(avcDecoder, mimeType);
|
||||
}
|
||||
else if ((videoFormat & MoonBridge.VIDEO_FORMAT_MASK_H265) != 0) {
|
||||
mimeType = "video/hevc";
|
||||
@@ -275,6 +282,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
}
|
||||
|
||||
refFrameInvalidationActive = refFrameInvalidationHevc;
|
||||
|
||||
lowLatency = MediaCodecHelper.decoderSupportsLowLatency(hevcDecoder, mimeType);
|
||||
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(hevcDecoder, mimeType);
|
||||
}
|
||||
else {
|
||||
// Unknown format
|
||||
@@ -293,6 +303,14 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
|
||||
MediaFormat videoFormat = MediaFormat.createVideoFormat(mimeType, width, height);
|
||||
|
||||
// Avoid setting KEY_FRAME_RATE on Lollipop and earlier to reduce compatibility risk
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// We use prefs.fps instead of redrawRate here because the low latency hack in Game.java
|
||||
// may leave us with an odd redrawRate value like 59 or 49 which might cause the decoder
|
||||
// to puke. To be safe, we'll use the unmodified value.
|
||||
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, prefs.fps);
|
||||
}
|
||||
|
||||
// Adaptive playback can also be enabled by the whitelist on pre-KitKat devices
|
||||
// so we don't fill these pre-KitKat
|
||||
if (adaptivePlayback && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
@@ -300,7 +318,22 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, height);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (lowLatency) {
|
||||
videoFormat.setInteger(MediaCodecHelper.KEY_LOW_LATENCY, 1);
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// Set the Qualcomm vendor low latency extension if the Android R option is unavailable
|
||||
if (MediaCodecHelper.decoderSupportsQcomVendorLowLatency(selectedDecoderName)) {
|
||||
// MediaCodec supports vendor-defined format keys using the "vendor.<extension name>.<parameter name>" syntax.
|
||||
// These allow access to functionality that is not exposed through documented MediaFormat.KEY_* values.
|
||||
// https://cs.android.com/android/platform/superproject/+/master:hardware/qcom/sdm845/media/mm-video-v4l2/vidc/common/inc/vidc_vendor_extensions.h;l=67
|
||||
//
|
||||
// Examples of Qualcomm's vendor extensions for Snapdragon 845:
|
||||
// https://cs.android.com/android/platform/superproject/+/master:hardware/qcom/sdm845/media/mm-video-v4l2/vidc/vdec/src/omx_vdec_extensions.hpp
|
||||
// https://cs.android.com/android/_/android/platform/hardware/qcom/sm8150/media/+/0621ceb1c1b19564999db8293574a0e12952ff6c
|
||||
videoFormat.setInteger("vendor.qti-ext-dec-low-latency.enable", 1);
|
||||
}
|
||||
|
||||
// Operate at maximum rate to lower latency as much as possible on
|
||||
// some Qualcomm platforms. We could also set KEY_PRIORITY to 0 (realtime)
|
||||
// but that will actually result in the decoder crashing if it can't satisfy
|
||||
@@ -308,8 +341,18 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
videoFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, Short.MAX_VALUE);
|
||||
}
|
||||
|
||||
configuredFormat = videoFormat;
|
||||
LimeLog.info("Configuring with format: "+configuredFormat);
|
||||
|
||||
try {
|
||||
videoDecoder.configure(videoFormat, renderTarget.getSurface(), null, 0);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// This will contain the actual accepted input format attributes
|
||||
inputFormat = videoDecoder.getInputFormat();
|
||||
LimeLog.info("Input format: "+inputFormat);
|
||||
}
|
||||
|
||||
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
||||
|
||||
if (USE_FRAME_RENDER_TIME && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
@@ -334,6 +377,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
legacyInputBuffers = videoDecoder.getInputBuffers();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return -5;
|
||||
@@ -445,7 +489,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
break;
|
||||
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
||||
LimeLog.info("Output format changed");
|
||||
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
|
||||
outputFormat = videoDecoder.getOutputFormat();
|
||||
LimeLog.info("New output format: " + outputFormat);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -719,7 +764,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
|
||||
// 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.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
sps.vuiParams.videoSignalTypePresentFlag = false;
|
||||
sps.vuiParams.colourDescriptionPresentFlag = false;
|
||||
sps.vuiParams.chromaLocInfoPresentFlag = false;
|
||||
@@ -1018,12 +1063,31 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.avcDecoder != null) {
|
||||
Range<Integer> avcWidthRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getSupportedWidths();
|
||||
str += "AVC supported width range: "+avcWidthRange.getLower()+" - "+avcWidthRange.getUpper()+"\n";
|
||||
str += "AVC supported width range: "+avcWidthRange+"\n";
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
try {
|
||||
Range<Double> avcFpsRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
||||
str += "AVC achievable FPS range: "+avcFpsRange+"\n";
|
||||
} catch (IllegalArgumentException e) {
|
||||
str += "AVC achievable FPS range: UNSUPPORTED!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.hevcDecoder != null) {
|
||||
Range<Integer> hevcWidthRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getSupportedWidths();
|
||||
str += "HEVC supported width range: "+hevcWidthRange.getLower()+" - "+hevcWidthRange.getUpper()+"\n";
|
||||
str += "HEVC supported width range: "+hevcWidthRange+"\n";
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
try {
|
||||
Range<Double> hevcFpsRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
||||
str += "HEVC achievable FPS range: " + hevcFpsRange + "\n";
|
||||
} catch (IllegalArgumentException e) {
|
||||
str += "HEVC achievable FPS range: UNSUPPORTED!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
str += "Configured format: "+renderer.configuredFormat+"\n";
|
||||
str += "Input format: "+renderer.inputFormat+"\n";
|
||||
str += "Output format: "+renderer.outputFormat+"\n";
|
||||
str += "Adaptive playback: "+renderer.adaptivePlayback+"\n";
|
||||
str += "GL Renderer: "+renderer.glRenderer+"\n";
|
||||
str += "Build fingerprint: "+Build.FINGERPRINT+"\n";
|
||||
@@ -1031,6 +1095,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||
str += "Consecutive crashes: "+renderer.consecutiveCrashCount+"\n";
|
||||
str += "RFI active: "+renderer.refFrameInvalidationActive+"\n";
|
||||
str += "Using modern SPS patching: "+(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)+"\n";
|
||||
str += "Low latency mode: "+renderer.lowLatency+"\n";
|
||||
str += "Video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
||||
str += "FPS target: "+renderer.refreshRate+"\n";
|
||||
str += "Bitrate: "+renderer.prefs.bitrate+" Kbps \n";
|
||||
|
||||
@@ -38,6 +38,11 @@ public class MediaCodecHelper {
|
||||
private static final List<String> refFrameInvalidationHevcPrefixes;
|
||||
private static final List<String> blacklisted49FpsDecoderPrefixes;
|
||||
private static final List<String> blacklisted59FpsDecoderPrefixes;
|
||||
private static final List<String> qualcommDecoderPrefixes;
|
||||
|
||||
// FIXME: Remove when Android R SDK is finalized
|
||||
public static final String FEATURE_LowLatency = "low-latency";
|
||||
public static final String KEY_LOW_LATENCY = "low-latency";
|
||||
|
||||
private static boolean isLowEndSnapdragon = false;
|
||||
private static boolean initialized = false;
|
||||
@@ -189,6 +194,13 @@ public class MediaCodecHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
qualcommDecoderPrefixes = new LinkedList<>();
|
||||
|
||||
qualcommDecoderPrefixes.add("omx.qcom");
|
||||
qualcommDecoderPrefixes.add("c2.qti");
|
||||
}
|
||||
|
||||
private static boolean isPowerVR(String glRenderer) {
|
||||
return glRenderer.toLowerCase().contains("powervr");
|
||||
}
|
||||
@@ -330,7 +342,24 @@ public class MediaCodecHelper {
|
||||
return System.nanoTime() / 1000000L;
|
||||
}
|
||||
|
||||
public static boolean decoderSupportsAdaptivePlayback(MediaCodecInfo decoderInfo) {
|
||||
public static boolean decoderSupportsLowLatency(MediaCodecInfo decoderInfo, String mimeType) {
|
||||
// KitKat added CodecCapabilities.isFeatureSupported()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
if (decoderInfo.getCapabilitiesForType(mimeType).isFeatureSupported(FEATURE_LowLatency)) {
|
||||
LimeLog.info("Low latency decoding mode supported (FEATURE_LowLatency)");
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Tolerate buggy codecs
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean decoderSupportsAdaptivePlayback(MediaCodecInfo decoderInfo, String mimeType) {
|
||||
// Possibly enable adaptive playback on KitKat and above
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (isDecoderInList(blacklistedAdaptivePlaybackPrefixes, decoderInfo.getName())) {
|
||||
@@ -339,7 +368,7 @@ public class MediaCodecHelper {
|
||||
}
|
||||
|
||||
try {
|
||||
if (decoderInfo.getCapabilitiesForType("video/avc").
|
||||
if (decoderInfo.getCapabilitiesForType(mimeType).
|
||||
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
|
||||
{
|
||||
// This will make getCapabilities() return that adaptive playback is supported
|
||||
@@ -348,12 +377,20 @@ public class MediaCodecHelper {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Tolerate buggy codecs
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean decoderSupportsQcomVendorLowLatency(String decoderName) {
|
||||
// MediaCodec vendor extension support was introduced in Android 8.0:
|
||||
// https://cs.android.com/android/_/android/platform/frameworks/av/+/01c10f8cdcd58d1e7025f426a72e6e75ba5d7fc2
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||
isDecoderInList(qualcommDecoderPrefixes, decoderName);
|
||||
}
|
||||
|
||||
public static boolean decoderNeedsConstrainedHighProfile(String decoderName) {
|
||||
return isDecoderInList(constrainedHighProfilePrefixes, decoderName);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ public class ConnectionContext {
|
||||
public String serverGfeVersion;
|
||||
|
||||
public int negotiatedWidth, negotiatedHeight;
|
||||
public int negotiatedFps;
|
||||
public boolean negotiatedHdr;
|
||||
|
||||
public int videoCapabilities;
|
||||
|
||||
@@ -121,13 +121,11 @@ public class NvConnection {
|
||||
// Lower resolution to 1080p
|
||||
context.negotiatedWidth = 1920;
|
||||
context.negotiatedHeight = 1080;
|
||||
context.negotiatedFps = context.streamConfig.getRefreshRate();
|
||||
}
|
||||
else {
|
||||
// Take what the client wanted
|
||||
context.negotiatedWidth = context.streamConfig.getWidth();
|
||||
context.negotiatedHeight = context.streamConfig.getHeight();
|
||||
context.negotiatedFps = context.streamConfig.getRefreshRate();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -263,9 +261,9 @@ public class NvConnection {
|
||||
int ret = MoonBridge.startConnection(context.serverAddress,
|
||||
context.serverAppVersion, context.serverGfeVersion,
|
||||
context.negotiatedWidth, context.negotiatedHeight,
|
||||
context.negotiatedFps, context.streamConfig.getBitrate(),
|
||||
context.streamConfig.getRefreshRate(), context.streamConfig.getBitrate(),
|
||||
context.streamConfig.getMaxPacketSize(),
|
||||
context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration(),
|
||||
context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration().toInt(),
|
||||
context.streamConfig.getHevcSupported(),
|
||||
context.negotiatedHdr,
|
||||
context.streamConfig.getHevcBitratePercentageMultiplier(),
|
||||
|
||||
@@ -3,10 +3,10 @@ package com.limelight.nvstream;
|
||||
public interface NvConnectionListener {
|
||||
void stageStarting(String stage);
|
||||
void stageComplete(String stage);
|
||||
void stageFailed(String stage, long errorCode);
|
||||
void stageFailed(String stage, int errorCode);
|
||||
|
||||
void connectionStarted();
|
||||
void connectionTerminated(long errorCode);
|
||||
void connectionTerminated(int errorCode);
|
||||
void connectionStatusUpdate(int connectionStatus);
|
||||
|
||||
void displayMessage(String message);
|
||||
|
||||
@@ -9,16 +9,11 @@ public class StreamConfiguration {
|
||||
public static final int STREAM_CFG_LOCAL = 0;
|
||||
public static final int STREAM_CFG_REMOTE = 1;
|
||||
public static final int STREAM_CFG_AUTO = 2;
|
||||
|
||||
private static final int CHANNEL_COUNT_STEREO = 2;
|
||||
private static final int CHANNEL_COUNT_5_1 = 6;
|
||||
|
||||
private static final int CHANNEL_MASK_STEREO = 0x3;
|
||||
private static final int CHANNEL_MASK_5_1 = 0xFC;
|
||||
|
||||
private NvApp app;
|
||||
private int width, height;
|
||||
private int refreshRate;
|
||||
private int launchRefreshRate;
|
||||
private int clientRefreshRateX100;
|
||||
private int bitrate;
|
||||
private boolean sops;
|
||||
@@ -26,9 +21,7 @@ public class StreamConfiguration {
|
||||
private boolean playLocalAudio;
|
||||
private int maxPacketSize;
|
||||
private int remote;
|
||||
private int audioChannelMask;
|
||||
private int audioChannelCount;
|
||||
private int audioConfiguration;
|
||||
private MoonBridge.AudioConfiguration audioConfiguration;
|
||||
private boolean supportsHevc;
|
||||
private int hevcBitratePercentageMultiplier;
|
||||
private boolean enableHdr;
|
||||
@@ -57,6 +50,11 @@ public class StreamConfiguration {
|
||||
config.refreshRate = refreshRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamConfiguration.Builder setLaunchRefreshRate(int refreshRate) {
|
||||
config.launchRefreshRate = refreshRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamConfiguration.Builder setBitrate(int bitrate) {
|
||||
config.bitrate = bitrate;
|
||||
@@ -113,21 +111,8 @@ public class StreamConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamConfiguration.Builder setAudioConfiguration(int audioConfig) {
|
||||
if (audioConfig == MoonBridge.AUDIO_CONFIGURATION_STEREO) {
|
||||
config.audioChannelCount = CHANNEL_COUNT_STEREO;
|
||||
config.audioChannelMask = CHANNEL_MASK_STEREO;
|
||||
}
|
||||
else if (audioConfig == MoonBridge.AUDIO_CONFIGURATION_51_SURROUND) {
|
||||
config.audioChannelCount = CHANNEL_COUNT_5_1;
|
||||
config.audioChannelMask = CHANNEL_MASK_5_1;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid audio configuration");
|
||||
}
|
||||
|
||||
public StreamConfiguration.Builder setAudioConfiguration(MoonBridge.AudioConfiguration audioConfig) {
|
||||
config.audioConfiguration = audioConfig;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -147,13 +132,13 @@ public class StreamConfiguration {
|
||||
this.width = 1280;
|
||||
this.height = 720;
|
||||
this.refreshRate = 60;
|
||||
this.launchRefreshRate = 60;
|
||||
this.bitrate = 10000;
|
||||
this.maxPacketSize = 1024;
|
||||
this.remote = STREAM_CFG_AUTO;
|
||||
this.sops = true;
|
||||
this.enableAdaptiveResolution = false;
|
||||
this.audioChannelCount = CHANNEL_COUNT_STEREO;
|
||||
this.audioChannelMask = CHANNEL_MASK_STEREO;
|
||||
this.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_STEREO;
|
||||
this.supportsHevc = false;
|
||||
this.enableHdr = false;
|
||||
this.attachedGamepadMask = 0;
|
||||
@@ -170,6 +155,10 @@ public class StreamConfiguration {
|
||||
public int getRefreshRate() {
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
public int getLaunchRefreshRate() {
|
||||
return launchRefreshRate;
|
||||
}
|
||||
|
||||
public int getBitrate() {
|
||||
return bitrate;
|
||||
@@ -198,16 +187,8 @@ public class StreamConfiguration {
|
||||
public int getRemote() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
public int getAudioChannelCount() {
|
||||
return audioChannelCount;
|
||||
}
|
||||
|
||||
public int getAudioChannelMask() {
|
||||
return audioChannelMask;
|
||||
}
|
||||
|
||||
public int getAudioConfiguration() {
|
||||
public MoonBridge.AudioConfiguration getAudioConfiguration() {
|
||||
return audioConfiguration;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.limelight.nvstream.av.audio;
|
||||
|
||||
import com.limelight.nvstream.jni.MoonBridge;
|
||||
|
||||
public interface AudioRenderer {
|
||||
int setup(int audioConfiguration, int sampleRate, int samplesPerFrame);
|
||||
int setup(MoonBridge.AudioConfiguration audioConfiguration, int sampleRate, int samplesPerFrame);
|
||||
|
||||
void start();
|
||||
|
||||
|
||||
@@ -624,7 +624,7 @@ public class NvHTTP {
|
||||
// Using an FPS value over 60 causes SOPS to default to 720p60,
|
||||
// so force it to 60 when starting. This won't impact our ability
|
||||
// to get > 60 FPS while actually streaming though.
|
||||
int fps = context.negotiatedFps > 60 ? 60 : context.negotiatedFps;
|
||||
int fps = context.streamConfig.getLaunchRefreshRate() > 60 ? 60 : context.streamConfig.getLaunchRefreshRate();
|
||||
|
||||
// Using an unsupported resolution (not 720p, 1080p, or 4K) causes
|
||||
// GFE to force SOPS to 720p60. This is fine for < 720p resolutions like
|
||||
@@ -648,7 +648,7 @@ public class NvHTTP {
|
||||
"&rikeyid="+context.riKeyId +
|
||||
(!enableHdr ? "" : "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0") +
|
||||
"&localAudioPlayMode=" + (context.streamConfig.getPlayLocalAudio() ? 1 : 0) +
|
||||
"&surroundAudioInfo=" + ((context.streamConfig.getAudioChannelMask() << 16) + context.streamConfig.getAudioChannelCount()) +
|
||||
"&surroundAudioInfo=" + context.streamConfig.getAudioConfiguration().getSurroundAudioInfo() +
|
||||
(context.streamConfig.getAttachedGamepadMask() != 0 ? "&remoteControllersBitmap=" + context.streamConfig.getAttachedGamepadMask() : "") +
|
||||
(context.streamConfig.getAttachedGamepadMask() != 0 ? "&gcmap=" + context.streamConfig.getAttachedGamepadMask() : ""),
|
||||
false);
|
||||
@@ -660,7 +660,7 @@ public class NvHTTP {
|
||||
String xmlStr = openHttpConnectionToString(baseUrlHttps + "/resume?" + buildUniqueIdUuidString() +
|
||||
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
||||
"&rikeyid="+context.riKeyId +
|
||||
"&surroundAudioInfo=" + ((context.streamConfig.getAudioChannelMask() << 16) + context.streamConfig.getAudioChannelCount()),
|
||||
"&surroundAudioInfo=" + context.streamConfig.getAudioConfiguration().getSurroundAudioInfo(),
|
||||
false);
|
||||
String resume = getXmlString(xmlStr, "resume");
|
||||
return Integer.parseInt(resume) != 0;
|
||||
|
||||
@@ -7,8 +7,9 @@ import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||
public class MoonBridge {
|
||||
/* See documentation in Limelight.h for information about these functions and constants */
|
||||
|
||||
public static final int AUDIO_CONFIGURATION_STEREO = 0;
|
||||
public static final int AUDIO_CONFIGURATION_51_SURROUND = 1;
|
||||
public static final AudioConfiguration AUDIO_CONFIGURATION_STEREO = new AudioConfiguration(2, 0x3);
|
||||
public static final AudioConfiguration AUDIO_CONFIGURATION_51_SURROUND = new AudioConfiguration(6, 0x3F);
|
||||
public static final AudioConfiguration AUDIO_CONFIGURATION_71_SURROUND = new AudioConfiguration(8, 0x63F);
|
||||
|
||||
public static final int VIDEO_FORMAT_H264 = 0x0001;
|
||||
public static final int VIDEO_FORMAT_H265 = 0x0100;
|
||||
@@ -45,6 +46,57 @@ public class MoonBridge {
|
||||
return slices << 24;
|
||||
}
|
||||
|
||||
public static class AudioConfiguration {
|
||||
public final int channelCount;
|
||||
public final int channelMask;
|
||||
|
||||
public AudioConfiguration(int channelCount, int channelMask) {
|
||||
this.channelCount = channelCount;
|
||||
this.channelMask = channelMask;
|
||||
}
|
||||
|
||||
// Creates an AudioConfiguration from the integer value returned by moonlight-common-c
|
||||
// See CHANNEL_COUNT_FROM_AUDIO_CONFIGURATION() and CHANNEL_MASK_FROM_AUDIO_CONFIGURATION()
|
||||
// in Limelight.h
|
||||
private AudioConfiguration(int audioConfiguration) {
|
||||
// Check the magic byte before decoding to make sure we got something that's actually
|
||||
// a MAKE_AUDIO_CONFIGURATION()-based value and not something else like an older version
|
||||
// hardcoded AUDIO_CONFIGURATION value from an earlier version of moonlight-common-c.
|
||||
if ((audioConfiguration & 0xFF) != 0xCA) {
|
||||
throw new IllegalArgumentException("Audio configuration has invalid magic byte!");
|
||||
}
|
||||
|
||||
this.channelCount = (audioConfiguration >> 8) & 0xFF;
|
||||
this.channelMask = (audioConfiguration >> 16) & 0xFFFF;
|
||||
}
|
||||
|
||||
// See SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION() in Limelight.h
|
||||
public int getSurroundAudioInfo() {
|
||||
return channelMask << 16 | channelCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof AudioConfiguration) {
|
||||
AudioConfiguration that = (AudioConfiguration)obj;
|
||||
return this.toInt() == that.toInt();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toInt();
|
||||
}
|
||||
|
||||
// Returns the integer value expected by moonlight-common-c
|
||||
// See MAKE_AUDIO_CONFIGURATION() in Limelight.h
|
||||
public int toInt() {
|
||||
return ((channelMask) << 16) | (channelCount << 8) | 0xCA;
|
||||
}
|
||||
}
|
||||
|
||||
public static int bridgeDrSetup(int videoFormat, int width, int height, int redrawRate) {
|
||||
if (videoRenderer != null) {
|
||||
return videoRenderer.setup(videoFormat, width, height, redrawRate);
|
||||
@@ -86,7 +138,7 @@ public class MoonBridge {
|
||||
|
||||
public static int bridgeArInit(int audioConfiguration, int sampleRate, int samplesPerFrame) {
|
||||
if (audioRenderer != null) {
|
||||
return audioRenderer.setup(audioConfiguration, sampleRate, samplesPerFrame);
|
||||
return audioRenderer.setup(new AudioConfiguration(audioConfiguration), sampleRate, samplesPerFrame);
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
@@ -129,7 +181,7 @@ public class MoonBridge {
|
||||
}
|
||||
}
|
||||
|
||||
public static void bridgeClStageFailed(int stage, long errorCode) {
|
||||
public static void bridgeClStageFailed(int stage, int errorCode) {
|
||||
if (connectionListener != null) {
|
||||
connectionListener.stageFailed(getStageName(stage), errorCode);
|
||||
}
|
||||
@@ -141,7 +193,7 @@ public class MoonBridge {
|
||||
}
|
||||
}
|
||||
|
||||
public static void bridgeClConnectionTerminated(long errorCode) {
|
||||
public static void bridgeClConnectionTerminated(int errorCode) {
|
||||
if (connectionListener != null) {
|
||||
connectionListener.connectionTerminated(errorCode);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.limelight.nvstream.jni.MoonBridge;
|
||||
|
||||
public class PreferenceConfiguration {
|
||||
private static final String LEGACY_RES_FPS_PREF_STRING = "list_resolution_fps";
|
||||
|
||||
@@ -19,11 +21,13 @@ public class PreferenceConfiguration {
|
||||
private static final String DISABLE_TOASTS_PREF_STRING = "checkbox_disable_warnings";
|
||||
private static final String HOST_AUDIO_PREF_STRING = "checkbox_host_audio";
|
||||
private static final String DEADZONE_PREF_STRING = "seekbar_deadzone";
|
||||
private static final String OSC_OPACITY_PREF_STRING = "seekbar_osc_opacity";
|
||||
private static final String LANGUAGE_PREF_STRING = "list_languages";
|
||||
private static final String LIST_MODE_PREF_STRING = "checkbox_list_mode";
|
||||
private static final String SMALL_ICONS_PREF_STRING = "checkbox_small_icon_mode";
|
||||
private static final String MULTI_CONTROLLER_PREF_STRING = "checkbox_multi_controller";
|
||||
private static final String ENABLE_51_SURROUND_PREF_STRING = "checkbox_51_surround";
|
||||
private static final String AUDIO_CONFIG_PREF_STRING = "list_audio_config";
|
||||
private static final String LEGACY_ENABLE_51_SURROUND_PREF_STRING = "checkbox_51_surround";
|
||||
private static final String USB_DRIVER_PREF_SRING = "checkbox_usb_driver";
|
||||
private static final String VIDEO_FORMAT_PREF_STRING = "video_format";
|
||||
private static final String ONSCREEN_CONTROLLER_PREF_STRING = "checkbox_show_onscreen_controls";
|
||||
@@ -46,10 +50,10 @@ public class PreferenceConfiguration {
|
||||
private static final boolean DEFAULT_DISABLE_TOASTS = false;
|
||||
private static final boolean DEFAULT_HOST_AUDIO = false;
|
||||
private static final int DEFAULT_DEADZONE = 15;
|
||||
private static final int DEFAULT_OPACITY = 90;
|
||||
public static final String DEFAULT_LANGUAGE = "default";
|
||||
private static final boolean DEFAULT_LIST_MODE = false;
|
||||
private static final boolean DEFAULT_MULTI_CONTROLLER = true;
|
||||
private static final boolean DEFAULT_ENABLE_51_SURROUND = false;
|
||||
private static final boolean DEFAULT_USB_DRIVER = true;
|
||||
private static final String DEFAULT_VIDEO_FORMAT = "auto";
|
||||
private static final boolean ONSCREEN_CONTROLLER_DEFAULT = false;
|
||||
@@ -64,6 +68,7 @@ public class PreferenceConfiguration {
|
||||
private static final boolean DEFAULT_UNLOCK_FPS = false;
|
||||
private static final boolean DEFAULT_VIBRATE_OSC = true;
|
||||
private static final boolean DEFAULT_VIBRATE_FALLBACK = false;
|
||||
private static final String DEFAULT_AUDIO_CONFIG = "2"; // Stereo
|
||||
|
||||
public static final int FORCE_H265_ON = -1;
|
||||
public static final int AUTOSELECT_H265 = 0;
|
||||
@@ -73,9 +78,10 @@ public class PreferenceConfiguration {
|
||||
public int bitrate;
|
||||
public int videoFormat;
|
||||
public int deadzonePercentage;
|
||||
public int oscOpacity;
|
||||
public boolean stretchVideo, enableSops, playHostAudio, disableWarnings;
|
||||
public String language;
|
||||
public boolean listMode, smallIconMode, multiController, enable51Surround, usbDriver;
|
||||
public boolean listMode, smallIconMode, multiController, usbDriver;
|
||||
public boolean onscreenController;
|
||||
public boolean onlyL3R3;
|
||||
public boolean disableFrameDrop;
|
||||
@@ -88,6 +94,7 @@ public class PreferenceConfiguration {
|
||||
public boolean unlockFps;
|
||||
public boolean vibrateOsc;
|
||||
public boolean vibrateFallbackToDevice;
|
||||
public MoonBridge.AudioConfiguration audioConfiguration;
|
||||
|
||||
private static int getHeightFromResolutionString(String resString) {
|
||||
if (resString.equalsIgnoreCase("360p")) {
|
||||
@@ -244,6 +251,15 @@ public class PreferenceConfiguration {
|
||||
PreferenceConfiguration config = new PreferenceConfiguration();
|
||||
|
||||
// Migrate legacy preferences to the new locations
|
||||
if (prefs.contains(LEGACY_ENABLE_51_SURROUND_PREF_STRING)) {
|
||||
if (prefs.getBoolean(LEGACY_ENABLE_51_SURROUND_PREF_STRING, false)) {
|
||||
prefs.edit()
|
||||
.remove(LEGACY_ENABLE_51_SURROUND_PREF_STRING)
|
||||
.putString(AUDIO_CONFIG_PREF_STRING, "51")
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
||||
String str = prefs.getString(LEGACY_RES_FPS_PREF_STRING, null);
|
||||
if (str != null) {
|
||||
if (str.equals("360p30")) {
|
||||
@@ -313,10 +329,23 @@ public class PreferenceConfiguration {
|
||||
config.bitrate = getDefaultBitrate(context);
|
||||
}
|
||||
|
||||
String audioConfig = prefs.getString(AUDIO_CONFIG_PREF_STRING, DEFAULT_AUDIO_CONFIG);
|
||||
if (audioConfig.equals("71")) {
|
||||
config.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_71_SURROUND;
|
||||
}
|
||||
else if (audioConfig.equals("51")) {
|
||||
config.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_51_SURROUND;
|
||||
}
|
||||
else /* if (audioConfig.equals("2")) */ {
|
||||
config.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_STEREO;
|
||||
}
|
||||
|
||||
config.videoFormat = getVideoFormatValue(context);
|
||||
|
||||
config.deadzonePercentage = prefs.getInt(DEADZONE_PREF_STRING, DEFAULT_DEADZONE);
|
||||
|
||||
config.oscOpacity = prefs.getInt(OSC_OPACITY_PREF_STRING, DEFAULT_OPACITY);
|
||||
|
||||
config.language = prefs.getString(LANGUAGE_PREF_STRING, DEFAULT_LANGUAGE);
|
||||
|
||||
// Checkbox preferences
|
||||
@@ -327,7 +356,6 @@ public class PreferenceConfiguration {
|
||||
config.listMode = prefs.getBoolean(LIST_MODE_PREF_STRING, DEFAULT_LIST_MODE);
|
||||
config.smallIconMode = prefs.getBoolean(SMALL_ICONS_PREF_STRING, getDefaultSmallMode(context));
|
||||
config.multiController = prefs.getBoolean(MULTI_CONTROLLER_PREF_STRING, DEFAULT_MULTI_CONTROLLER);
|
||||
config.enable51Surround = prefs.getBoolean(ENABLE_51_SURROUND_PREF_STRING, DEFAULT_ENABLE_51_SURROUND);
|
||||
config.usbDriver = prefs.getBoolean(USB_DRIVER_PREF_SRING, DEFAULT_USB_DRIVER);
|
||||
config.onscreenController = prefs.getBoolean(ONSCREEN_CONTROLLER_PREF_STRING, ONSCREEN_CONTROLLER_DEFAULT);
|
||||
config.onlyL3R3 = prefs.getBoolean(ONLY_L3_R3_PREF_STRING, ONLY_L3_R3_DEFAULT);
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.preference.DialogPreference;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@@ -78,6 +79,8 @@ public class SeekBarPreference extends DialogPreference
|
||||
valueText = new TextView(context);
|
||||
valueText.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
valueText.setTextSize(32);
|
||||
// Default text for value; hides bug where OnSeekBarChangeListener isn't called when opacity is 0%
|
||||
valueText.setText("0%");
|
||||
params = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
@@ -192,4 +192,13 @@ public class ShortcutHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void enableAppShortcut(ComputerDetails computer, NvApp app) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
String id = getShortcutIdForGame(computer, app);
|
||||
if (getInfoForId(id) != null) {
|
||||
sm.enableShortcuts(Collections.singletonList(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,9 +87,9 @@ Java_com_limelight_nvstream_jni_MoonBridge_init(JNIEnv *env, jclass clazz) {
|
||||
BridgeArPlaySampleMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArPlaySample", "([S)V");
|
||||
BridgeClStageStartingMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageStarting", "(I)V");
|
||||
BridgeClStageCompleteMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageComplete", "(I)V");
|
||||
BridgeClStageFailedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageFailed", "(IJ)V");
|
||||
BridgeClStageFailedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageFailed", "(II)V");
|
||||
BridgeClConnectionStartedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionStarted", "()V");
|
||||
BridgeClConnectionTerminatedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionTerminated", "(J)V");
|
||||
BridgeClConnectionTerminatedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionTerminated", "(I)V");
|
||||
BridgeClRumbleMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClRumble", "(SSS)V");
|
||||
BridgeClConnectionStatusUpdateMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionStatusUpdate", "(I)V");
|
||||
}
|
||||
@@ -98,12 +98,9 @@ int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void*
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
int err;
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSetupMethod, videoFormat, width, height, redrawRate);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
// This is called on a Java thread, so it's safe to return
|
||||
return -1;
|
||||
}
|
||||
else if (err != 0) {
|
||||
@@ -119,20 +116,12 @@ int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void*
|
||||
void BridgeDrStart(void) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStartMethod);
|
||||
}
|
||||
|
||||
void BridgeDrStop(void) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStopMethod);
|
||||
}
|
||||
|
||||
@@ -141,10 +130,6 @@ void BridgeDrCleanup(void) {
|
||||
|
||||
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrCleanupMethod);
|
||||
}
|
||||
|
||||
@@ -152,10 +137,6 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
int ret;
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return DR_OK;
|
||||
}
|
||||
|
||||
// Increase the size of our frame data buffer if our frame won't fit
|
||||
if ((*env)->GetArrayLength(env, DecodedFrameBuffer) < decodeUnit->fullLength) {
|
||||
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
||||
@@ -178,6 +159,8 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
||||
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
|
||||
decodeUnit->frameNumber, decodeUnit->receiveTimeMs);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
// We will crash here
|
||||
(*JVM)->DetachCurrentThread(JVM);
|
||||
return DR_OK;
|
||||
}
|
||||
else if (ret != DR_OK) {
|
||||
@@ -192,22 +175,27 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
||||
currentEntry = currentEntry->next;
|
||||
}
|
||||
|
||||
return (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
||||
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
||||
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
|
||||
decodeUnit->frameNumber,
|
||||
decodeUnit->receiveTimeMs);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
// We will crash here
|
||||
(*JVM)->DetachCurrentThread(JVM);
|
||||
return DR_OK;
|
||||
}
|
||||
else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int flags) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
int err;
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeArInitMethod, audioConfiguration, opusConfig->sampleRate, opusConfig->samplesPerFrame);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
// This is called on a Java thread, so it's safe to return
|
||||
err = -1;
|
||||
}
|
||||
if (err == 0) {
|
||||
@@ -233,20 +221,12 @@ int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusCon
|
||||
void BridgeArStart(void) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStartMethod);
|
||||
}
|
||||
|
||||
void BridgeArStop(void) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStopMethod);
|
||||
}
|
||||
|
||||
@@ -257,20 +237,12 @@ void BridgeArCleanup() {
|
||||
|
||||
(*env)->DeleteGlobalRef(env, DecodedAudioBuffer);
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArCleanupMethod);
|
||||
}
|
||||
|
||||
void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
jshort* decodedData = (*env)->GetShortArrayElements(env, DecodedAudioBuffer, 0);
|
||||
|
||||
int decodeLen = opus_multistream_decode(Decoder,
|
||||
@@ -284,6 +256,10 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
||||
(*env)->ReleaseShortArrayElements(env, DecodedAudioBuffer, decodedData, 0);
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArPlaySampleMethod, DecodedAudioBuffer);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
// We will crash here
|
||||
(*JVM)->DetachCurrentThread(JVM);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We can abort here to avoid the copy back since no data was modified
|
||||
@@ -294,71 +270,59 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
||||
void BridgeClStageStarting(int stage) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageStartingMethod, stage);
|
||||
}
|
||||
|
||||
void BridgeClStageComplete(int stage) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageCompleteMethod, stage);
|
||||
}
|
||||
|
||||
void BridgeClStageFailed(int stage, long errorCode) {
|
||||
void BridgeClStageFailed(int stage, int errorCode) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageFailedMethod, stage, errorCode);
|
||||
}
|
||||
|
||||
void BridgeClConnectionStarted(void) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStartedMethod, NULL);
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStartedMethod);
|
||||
}
|
||||
|
||||
void BridgeClConnectionTerminated(long errorCode) {
|
||||
void BridgeClConnectionTerminated(int errorCode) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionTerminatedMethod, errorCode);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
// We will crash here
|
||||
(*JVM)->DetachCurrentThread(JVM);
|
||||
}
|
||||
}
|
||||
|
||||
void BridgeClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
// The seemingly redundant short casts are required in order to convert the unsigned short to a signed short.
|
||||
// If we leave it as an unsigned short, CheckJNI will fail when the value exceeds 32767. The cast itself is
|
||||
// fine because the Java code treats the value as unsigned even though it's stored in a signed type.
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClRumbleMethod, controllerNumber, (short)lowFreqMotor, (short)highFreqMotor);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return;
|
||||
// We will crash here
|
||||
(*JVM)->DetachCurrentThread(JVM);
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClRumbleMethod, controllerNumber, lowFreqMotor, highFreqMotor);
|
||||
}
|
||||
|
||||
void BridgeClConnectionStatusUpdate(int connectionStatus) {
|
||||
JNIEnv* env = GetThreadEnv();
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStatusUpdateMethod, connectionStatus);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
// We will crash here
|
||||
(*JVM)->DetachCurrentThread(JVM);
|
||||
return;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStatusUpdateMethod, connectionStatus);
|
||||
}
|
||||
|
||||
void BridgeClLogMessage(const char* format, ...) {
|
||||
|
||||
Submodule app/src/main/jni/moonlight-core/moonlight-common-c updated: f5ae5df5d0...c2471157c0
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="decoder_names">
|
||||
<item>Decoder automatisch auswählen</item>
|
||||
<item>Software Decodierung erzwingen</item>
|
||||
<item>Hardware Decodierung erzwingen</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="video_format_names">
|
||||
<item>Verwende H.265 so fern stabile Unterstützung vorhanden ist</item>
|
||||
<item>Immer H.265 verwenden (könnte Crashes verursachen)</item>
|
||||
<item>Nie H.265 verwenden</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -0,0 +1,190 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Shortcut strings -->
|
||||
<string name="scut_deleted_pc">PC gelöscht</string>
|
||||
<string name="scut_not_paired">PC nicht verbunden</string>
|
||||
<string name="scut_pc_not_found">PC nicht gefunden</string>
|
||||
<string name="scut_invalid_uuid">Angegebener PC ist unzulässig</string>
|
||||
<string name="scut_invalid_app_id">Angegebene App ist unzulässig</string>
|
||||
|
||||
<!-- Help strings -->
|
||||
<string name="help_loading_title">Hilfsanzeige</string>
|
||||
<string name="help_loading_msg">Lade Hilfesanzeige…</string>
|
||||
|
||||
<!-- PC view menu entries -->
|
||||
<string name="pcview_menu_app_list">Spiele List anzeigen</string>
|
||||
<string name="pcview_menu_pair_pc">Mit PC verbinden</string>
|
||||
<string name="pcview_menu_unpair_pc">Verbindung beenden</string>
|
||||
<string name="pcview_menu_send_wol">Wake-On-LAN Anfrage senden</string>
|
||||
<string name="pcview_menu_delete_pc">PC löschen</string>
|
||||
<string name="pcview_menu_details">Details anzeigen</string>
|
||||
|
||||
<!-- Pair messages -->
|
||||
<string name="pairing">Verbinden…</string>
|
||||
<string name="pair_pc_offline">Computer ist offline</string>
|
||||
<string name="pair_pc_ingame">Am Computer ist bereits ein Spiel aktiv. Sie müssen das Spiel beenden bevor sie sich koppeln können.</string>
|
||||
<string name="pair_pairing_title">Verbinden</string>
|
||||
<string name="pair_pairing_msg">Bitte geben sie folgenden PIN auf ihrem PC ein:</string>
|
||||
<string name="pair_incorrect_pin">PIN inkorrekt</string>
|
||||
<string name="pair_fail">Verbindung fehlgeschlagen</string>
|
||||
<string name="pair_already_in_progress">Verbindungsaufbau rebreits im gange</string>
|
||||
|
||||
<!-- WOL messages -->
|
||||
<string name="wol_pc_online">Computer ist online</string>
|
||||
<string name="wol_no_mac">PC konnte nicht geweckt werden, da GFE keine MAC-Adresse gesendet hat.</string>
|
||||
<string name="wol_waking_pc">PC wird geweckt…</string>
|
||||
<string name="wol_waking_msg">Es kann einige Momente dauert ihren PC zu wecken.
|
||||
Sollte dies fehlschlagen stellen sie bitte sicher, dass Wake-On-LAN korrekt konfiguriert ist.
|
||||
</string>
|
||||
<string name="wol_fail">Sender der Wake-On-LAN Pakete ist fehlgeschlagen</string>
|
||||
|
||||
<!-- Unpair messages -->
|
||||
<string name="unpairing">Trenne Verbindung…</string>
|
||||
<string name="unpair_success">Verbindung erfolgreich getrennt</string>
|
||||
<string name="unpair_fail">Trennung der Verbindung fehlgeschlagen</string>
|
||||
<string name="unpair_error">Gerät wurde nicht verbunden</string>
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_pc_offline">Computer ist offline</string>
|
||||
<string name="error_manager_not_running">Der ComputerManager Service läuft nicht. Bitte warten sie ein paar Sekunden oder starten sie Die App neu.</string>
|
||||
<string name="error_unknown_host">Host konnte nicht aufgelößt werden.</string>
|
||||
<string name="error_404">GFE ist auf einen HTTP 404 Fehler gestoßen. Stellen sie sicher, dass die GPU ihres PCs unterstützt wird.
|
||||
Die verwendung von Remote-Desktop Software kann ebenso diesen Fehler verursachen. Starten sie ihren Computer neu oder reinstallieren sie GFE.
|
||||
</string>
|
||||
<string name="title_decoding_error">Video Decoder ist gecrashed</string>
|
||||
<string name="message_decoding_error">Moonlight ist wegen einer Inkompatibilität zu dem Video-Decoder ihres Gerätes gecrasht. Stellen sie sicher, dass GeForce Experience Version auf ihrem PC auf dem neuesten Stand ist. Sollten weiterin Crashes auftreten, versuchen sie ihre Stream Einstellungen zu justieren.</string>
|
||||
<string name="title_decoding_reset">Video Einstellungen zurücksetzen</string>
|
||||
<string name="message_decoding_reset">Der Video-Decoder ihres Geärts ist wiederholt mit den ausgewählten Einstellungen gecrasht. Ihre streaming einstellungen wurden zurückgesetzt.</string>
|
||||
<string name="error_usb_prohibited">USB Zugriff ist administrativ unterbunden. Bitte überprüfen sie ihre Knox oder MDM Einstellungen.</string>
|
||||
<string name="unable_to_pin_shortcut">Die zur Zeit aktive Launcher App ünterstützt das erstellen angehefteter Shortcuts nicht.</string>
|
||||
|
||||
<!-- Start application messages -->
|
||||
<string name="conn_establishing_title">Verbindung herstellen</string>
|
||||
<string name="conn_establishing_msg">Verbindung starten</string>
|
||||
<string name="conn_metered">Warnung: Das Datentransfairvolument ihrer Netzwerkverbindung ist limitiert!</string>
|
||||
<string name="conn_client_latency">Durchschnittliche Frame-Dekodierungslatenz:</string>
|
||||
<string name="conn_client_latency_hw">Hardware-Dekodierungslatenz:</string>
|
||||
<string name="conn_hardware_latency">Durchschnittliche Hardware-Dekodierungslatenz:</string>
|
||||
<string name="conn_starting">Startet</string>
|
||||
<string name="conn_error_title">Verbindingsfehler</string>
|
||||
<string name="conn_error_msg">Start fehlgeschlagen</string>
|
||||
<string name="conn_terminated_title">Verbindung beendet</string>
|
||||
<string name="conn_terminated_msg">Die verbinding wurde beendet</string>
|
||||
|
||||
<!-- General strings -->
|
||||
<string name="ip_hint">IP-Adresse des GeForce PCs</string>
|
||||
<string name="searching_pc">Suche nach PCs wo GeForce Experience aktiv ist…</string>
|
||||
<string name="yes">Ja</string>
|
||||
<string name="no">Nein</string>
|
||||
<string name="lost_connection">Verbindung zum PC verloren</string>
|
||||
<string name="title_details">Details</string>
|
||||
<string name="help">Hilfe</string>
|
||||
<string name="delete_pc_msg">Sind sie sicher, dass sie diesen PC löschen möchten?</string>
|
||||
<string name="slow_connection_msg">Langsame Verbindung zum PC\nReduzieren sie die Bitrate</string>
|
||||
<string name="poor_connection_msg">Sehr langsame Verbindung zum PC</string>
|
||||
<string name="perf_overlay_text">Videodimensionen: %1$s\nDecoder: %2$s\nGeschätzte PC Bildwiederholrate: %3$.2f FPS\nBildwiederholrate der Netzwerkübertragung: %4$.2f FPS\nWiedergabe-Bildwiederholungsrate: %5$.2f FPS\nWegen Netzwerkübertraung ausgelassene Frames: %6$.2f%%\nDurchschnittliche Übertragunsdauer: %7$.2f ms\nDurchschnittliche decoding dauer: %8$.2f ms</string>
|
||||
|
||||
<!-- AppList activity -->
|
||||
<string name="applist_connect_msg">Verbinde mit PC…</string>
|
||||
<string name="applist_menu_resume">Sitzung Wiederherstellen</string>
|
||||
<string name="applist_menu_quit">Sitzung Beenden</string>
|
||||
<string name="applist_menu_quit_and_start">Aktuelles Spiel beenden und Starte</string>
|
||||
<string name="applist_menu_cancel">Abbrechen</string>
|
||||
<string name="applist_menu_details">Details anzeigen</string>
|
||||
<string name="applist_menu_scut">Shortcut erstellen</string>
|
||||
<string name="applist_menu_tv_channel">Zu Kanal hinzufügen</string>
|
||||
<string name="applist_refresh_title">App Liste</string>
|
||||
<string name="applist_refresh_msg">Aktualisiere Apps…</string>
|
||||
<string name="applist_refresh_error_title">Fehler</string>
|
||||
<string name="applist_refresh_error_msg">Abfrage der App-Liste fehlgeschlagen</string>
|
||||
<string name="applist_quit_app">Beenden</string>
|
||||
<string name="applist_quit_success">Erfolgreich Beendet</string>
|
||||
<string name="applist_quit_fail">Beenden fehlgeschlagen</string>
|
||||
<string name="applist_quit_confirmation">Sind sie sicher, dass die die laufende App schließen möchten? Ungespeicherte Daten gehen verloren.</string>
|
||||
<string name="applist_details_id">App ID:</string>
|
||||
|
||||
<!-- Add computer manually activity -->
|
||||
<string name="title_add_pc">PC manuell hinzufügen</string>
|
||||
<string name="msg_add_pc">Verbinde zum PC…</string>
|
||||
<string name="addpc_fail">Verbindung fehlgeschlagen. Stellen sie sicher, dass die benötigten ports von einer Firewall gefiltert werden.</string>
|
||||
<string name="addpc_success">Computer erfolgreich hinzugefügt</string>
|
||||
<string name="addpc_unknown_host">Auflößen der PC-Adresse fehlgeschlagen. Stellen sie sicher, dass die Adresse keine Tippfehler beinhaltet.</string>
|
||||
<string name="addpc_enter_ip">Sie müssen eine IP-Addresse eingeben.</string>
|
||||
<string name="addpc_wrong_sitelocal">Diese Adresse sieht falsch aus. Sie müssen die öffentliche IP-Adresse ihreres Routers benutzen um über das Internet zu streamen.</string>
|
||||
|
||||
<!-- Preferences -->
|
||||
<string name="category_basic_settings">Allgemeine Einstellungen</string>
|
||||
<string name="title_resolution_list">Videoauflösung</string>
|
||||
<string name="summary_resolution_list">Ehöhen für klarere Bilder. Reduzieren für flüssigere Darstellung auf langsameren Geräten und Netzwerken.</string>
|
||||
<string name="title_fps_list">Bildwiederholungsrate</string>
|
||||
<string name="summary_fps_list">Erhöhen für einen gleichmäßigeren Video-Stream. Verringern um auf langsameren Geräten eine bessere Performance zu erzielen.</string>
|
||||
<string name="title_seekbar_bitrate">Video bitrate</string>
|
||||
<string name="summary_seekbar_bitrate">Erhöhen für einen schärferen Video-Stream. Verringern um auf langsameren Geräten eine bessere Performance zu erzielen.</string>
|
||||
<string name="suffix_seekbar_bitrate">Kbps</string>
|
||||
<string name="title_unlock_fps">Alle Bildwiederholungsraten freigeben</string>
|
||||
<string name="summary_unlock_fps">Streaming mit 90 oder 120 FPS reduziert gegebenfalls die Latenz auf High-End Geräten, für jedoch zu Crashes oder Lag auf Geräten die dies nicht untersützen können.</string>
|
||||
<string name="title_checkbox_stretch_video">Video auf den ganzen Bildschirm ausdehnen</string>
|
||||
<string name="title_checkbox_disable_warnings">Warnhinweise deaktivieren</string>
|
||||
<string name="summary_checkbox_disable_warnings">Verbindungswarnungen nicht als Overlay wärend des Streamens anzeigen</string>
|
||||
<string name="title_checkbox_enable_pip">Bild-in-Bild Überwachungsmodus aktivieren</string>
|
||||
<string name="summary_checkbox_enable_pip">Stream auch während des Multitaskings anzeigen (ohne Steuerung)</string>
|
||||
|
||||
<string name="category_audio_settings">Audio Einstellungen</string>
|
||||
|
||||
<string name="category_input_settings">Eingabe Einstellungen</string>
|
||||
<string name="title_checkbox_multi_controller">Automatische GamePad-Erkennung</string>
|
||||
<string name="summary_checkbox_multi_controller">Abwählen dieser Option erzwingt dass immer ein GamePad present ist</string>
|
||||
<string name="title_checkbox_vibrate_fallback">Vibrationsemulation aktivieren</string>
|
||||
<string name="summary_checkbox_vibrate_fallback">Lässt das Gerät vibrieren falls das GamePad keine Vibration unterstütz</string>
|
||||
<string name="title_seekbar_deadzone">Tot Bereich des Analogsticks</string>
|
||||
<string name="suffix_seekbar_deadzone">%</string>
|
||||
<string name="title_checkbox_xb1_driver">Xbox 360/One GamePad Treiber</string>
|
||||
<string name="summary_checkbox_xb1_driver">Aktiviert eingebauten USB Treiber für Geräte die keinen Xbox GamePad-Unterstützung haben</string>
|
||||
<string name="title_checkbox_usb_bind_all">Android GamePad Unterstütung überlagern</string>
|
||||
<string name="summary_checkbox_usb_bind_all">Erzwingt die Vernwedung von Moonlight\'s USB Treiber für alle Xbox kompatiblen GamePads</string>
|
||||
<string name="title_checkbox_mouse_emulation">Maus Emulation via GamePad</string>
|
||||
<string name="summary_checkbox_mouse_emulation">Langes gedrückt halten der Start-Taste wechselt in den Maus Modus</string>
|
||||
<string name="title_checkbox_mouse_nav_buttons">Vor- und Zurück-Tasten aktivieren</string>
|
||||
<string name="summary_checkbox_mouse_nav_buttons">Aktivierung dieser Option kann auf fehleranfälligen Geräten Rechts-Clicks verunmöglichen</string>
|
||||
|
||||
<string name="category_on_screen_controls_settings">On-Screen Steuerungseinstellungen</string>
|
||||
<string name="title_checkbox_show_onscreen_controls">Zeige On-Screen Steuerung</string>
|
||||
<string name="summary_checkbox_show_onscreen_controls">Zeige virtuelle Steuerelemente als Overlay auf dem Touchscreen</string>
|
||||
<string name="title_checkbox_vibrate_osc">Vibrationen aktivieren</string>
|
||||
<string name="summary_checkbox_vibrate_osc">Vibrieren als haptisches Feedback für die On-Screen Steuerelemente</string>
|
||||
<string name="title_only_l3r3">Nur L3 und R3 anzeigen</string>
|
||||
<string name="summary_only_l3r3">Alle virtuellen Steuerelemente außer L3 und R3 verbregen.</string>
|
||||
<string name="title_reset_osc">Gespeicherte On-Screen Steuerungelemente zurücksetzen</string>
|
||||
<string name="summary_reset_osc">Alle On-Screen Steuerelemente auf ihre Vorgabegröße und Position zurück setzen</string>
|
||||
<string name="dialog_title_reset_osc">Layout Zurücksetzen</string>
|
||||
<string name="dialog_text_reset_osc">Sind sie sicher, dass sie das gepseicherte on-screen Layout der Steuerelemte löschen wollen?</string>
|
||||
<string name="toast_reset_osc_success">On-Screen Steuerelemente wurden zurückgesetzt</string>
|
||||
|
||||
<string name="category_ui_settings">UI Einstellungen</string>
|
||||
<string name="title_language_list">Sprache</string>
|
||||
<string name="summary_language_list">Sprache die Moonlight verwenden soll</string>
|
||||
<string name="title_checkbox_list_mode">Zeige Listen anstelle von Rastern.</string>
|
||||
<string name="summary_checkbox_list_mode">Zeige Apps und PCs als Liste anstelle eines Rasters</string>
|
||||
<string name="title_checkbox_small_icon_mode">Verwende kleine Icons</string>
|
||||
<string name="summary_checkbox_small_icon_mode">Verwende kleine Icons in der Rasteransicht, um mehr gleichzeitig anzeigen zu können</string>
|
||||
|
||||
<string name="category_host_settings">Host Einstellungen</string>
|
||||
<string name="title_checkbox_enable_sops">Spieleinstellungen optimieren</string>
|
||||
<string name="summary_checkbox_enable_sops">GFE erlauben die die Spieleinstellungen zu optimieren.</string>
|
||||
<string name="title_checkbox_host_audio">Audio auf dem PC wiedergeben</string>
|
||||
<string name="summary_checkbox_host_audio">Audio auf dem PC und auf diesem Gerät wiedergeben</string>
|
||||
|
||||
<string name="category_advanced_settings">Erweiterte Einstellungen</string>
|
||||
<string name="title_disable_frame_drop">Nie Frames Überspringen</string>
|
||||
<string name="summary_disable_frame_drop">Kann potentiell das Mikro-Ruckeln auf einigen Geräten reduzieren, allerdings erhöt dies gleichzeitig die Latenz</string>
|
||||
<string name="title_video_format">Ändere H.265 Einstellungen</string>
|
||||
<string name="summary_video_format">H.265 verringerd die Video-Bandbreitenanforderung, funktioniert allerdings nur auf sehr neuen Geräten</string>
|
||||
<string name="title_enable_hdr">HDR aktivieren (experimentell)</string>
|
||||
<string name="summary_enable_hdr">HDR-Streaming sofern dies von der PC GPU unterstützt wird. HDR erfordert eine GPU der GTX 1000 Serie oder neuer.</string>
|
||||
<string name="title_enable_perf_overlay">Performance Overlay aktivieren</string>
|
||||
<string name="summary_enable_perf_overlay">Leistungsmerkmale während des Streamens in Echtzeit einblenden.</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
<string name="dialog_title_osc_opacity">Transparenz</string>
|
||||
|
||||
</resources>
|
||||
@@ -94,8 +94,6 @@
|
||||
<string name="summary_checkbox_disable_warnings">Desactivar mensajes de advertencia en pantalla durante la transmisión</string>
|
||||
|
||||
<string name="category_audio_settings">Configuración de audio</string>
|
||||
<string name="title_checkbox_51_surround">Activar sonido 5.1 surround</string>
|
||||
<string name="summary_checkbox_51_surround">Desmarcar si experimentas problemas de audio. Requiere GFE 2.7 o superior.</string>
|
||||
|
||||
<string name="title_checkbox_multi_controller">Soporte para múltiples mandos</string>
|
||||
<string name="summary_checkbox_multi_controller">Si no está marcado, todos los mandos aparecen como uno solo</string>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="fps_names">
|
||||
<item>30 IPS</item>
|
||||
<item>60 IPS</item>
|
||||
<item>90 IPS</item>
|
||||
<item>120 IPS</item>
|
||||
</string-array>
|
||||
<string-array name="audio_config_names">
|
||||
<item>Stéréo</item>
|
||||
<item>Son surround 5.1</item>
|
||||
<item>Son surround 7.1</item>
|
||||
</string-array>
|
||||
<string-array name="decoder_names">
|
||||
<item>Sélection automatique du décodeur</item>
|
||||
<item>Contraindre le décodage logiciel</item>
|
||||
<item>Contraindre le décodage matériel</item>
|
||||
</string-array>
|
||||
<string-array name="video_format_names">
|
||||
<item>Utiliser H.265 uniquement s\'il est stable</item>
|
||||
<item>Utilisez toujours H.265 (mais il peut planter)</item>
|
||||
<item>N\'utilisez jamais H.265</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -53,7 +53,7 @@
|
||||
L\'utilisation d\'un logiciel de bureau à distance peut également provoquer cette erreur. Essayez de redémarrer votre machine ou de réinstaller GFE.
|
||||
</string>
|
||||
<string name="title_decoding_error">Le décodeur vidéo s\'est écrasé</string>
|
||||
<string name="message_decoding_error">Moonlight s\'est écrasé en raison d\'une incompatibilité avec le décodeur vidéo de cet appareil. Assurez-vous que GeForce Experience soit mis à jour vers la dernière version sur votre PC. Essayez de régler les paramètres de diffusion si les plantages continuent.</string>
|
||||
<string name="message_decoding_error">Moonlight s\'est arrêté en raison d\'une incompatibilité avec le décodeur vidéo de cet appareil. Assurez-vous que GeForce Experience soit mis à jour vers la dernière version sur votre PC. Essayez de régler les paramètres de diffusion si les plantages continuent.</string>
|
||||
<string name="title_decoding_reset">Paramètres vidéo réinitialiser</string>
|
||||
<string name="message_decoding_reset">Le décodeur vidéo de votre appareil continue de planter avec les paramètres de diffusion sélectionnés. Vos paramètres de diffusion ont été réinitialisés par défaut.</string>
|
||||
<string name="error_usb_prohibited">L\'accès USB est interdit par votre appareil. Vérifiez vos paramètres Knox ou MDM.</string>
|
||||
@@ -132,14 +132,14 @@
|
||||
<string name="summary_checkbox_enable_pip">Permet de visualiser le flux (sans le contrôleur) tout en multitâche</string>
|
||||
|
||||
<string name="category_audio_settings">Paramètres audio</string>
|
||||
<string name="title_checkbox_51_surround">Activer son surround 5.1</string>
|
||||
<string name="summary_checkbox_51_surround">Décochez si vous rencontrez des problèmes audio. Nécessite GFE 2.7 ou supérieur.</string>
|
||||
<string name="title_audio_config_list">Configuration son surround</string>
|
||||
<string name="summary_audio_config_list">Activer le son surround 5.1 ou 7.1 pour les systèmes home cinéma</string>
|
||||
|
||||
<string name="category_input_settings">Paramètres d\'entrée</string>
|
||||
<string name="title_checkbox_multi_controller">Prise en charge de plusieurs contrôleurs</string>
|
||||
<string name="summary_checkbox_multi_controller">Lorsqu\'elle n\'est pas cochée, tous les contrôleurs sont regroupés</string>
|
||||
<string name="title_checkbox_vibrate_fallback">Emuler le support vibration</string>
|
||||
<string name="summary_checkbox_vibrate_fallback">Vibre votre appareil pour émuler une vibration si votre manette ne le prend pas en charge</string>
|
||||
<string name="title_checkbox_vibrate_fallback">Emuler support vibration de secours</string>
|
||||
<string name="summary_checkbox_vibrate_fallback">Emuler des tremblements si votre manette ne le prend pas en charge</string>
|
||||
<string name="title_seekbar_deadzone">Régler la zone morte du stick analogique</string>
|
||||
<string name="suffix_seekbar_deadzone">%</string>
|
||||
<string name="title_checkbox_xb1_driver">Pilote de contrôleur Xbox 360/One</string>
|
||||
@@ -148,14 +148,14 @@
|
||||
<string name="summary_checkbox_usb_bind_all">Force le pilote USB de Moonlight à prendre en charge tous les gamepads Xbox pris en charge</string>
|
||||
<string name="title_checkbox_mouse_emulation">Emulation de la souris via le gamepad</string>
|
||||
<string name="summary_checkbox_mouse_emulation">Appuyez longuement sur le bouton Start pour faire basculer la manette de jeu en mode souris.</string>
|
||||
<string name="title_checkbox_mouse_nav_buttons">Activer les boutons de souris arrière et avant</string>
|
||||
<string name="summary_checkbox_mouse_nav_buttons">L\'activation de cette option peut entraîner un clic droit sur certains périphériques.</string>
|
||||
<string name="title_checkbox_mouse_nav_buttons">Activer les boutons de la souris arrière et avant</string>
|
||||
<string name="summary_checkbox_mouse_nav_buttons">L\' activation de cette option peut entraîner un clic droit sur certains périphériques.</string>
|
||||
|
||||
<string name="category_on_screen_controls_settings">Paramètres des contrôles à l\'écran</string>
|
||||
<string name="title_checkbox_show_onscreen_controls">Afficher les commandes à l\'écran</string>
|
||||
<string name="summary_checkbox_show_onscreen_controls">Afficher la superposition du contrôleur virtuel sur l\'écran tactile</string>
|
||||
<string name="title_checkbox_vibrate_osc">Activer les vibrations</string>
|
||||
<string name="summary_checkbox_vibrate_osc">Vibre votre appareil pour émuler les vibrations des commandes à l\'écran</string>
|
||||
<string name="summary_checkbox_vibrate_osc">Emuler des tremblements des commandes à l\'écran</string>
|
||||
<string name="title_only_l3r3">Montre seulement L3 et R3</string>
|
||||
<string name="summary_only_l3r3">Cacher tout sauf L3 et R3</string>
|
||||
<string name="title_reset_osc">Effacer la disposition des commandes à l\'écran sauvegardée</string>
|
||||
@@ -163,6 +163,10 @@
|
||||
<string name="dialog_title_reset_osc">Réinitialiser la mise en page</string>
|
||||
<string name="dialog_text_reset_osc">Êtes-vous sûr de vouloir supprimer la disposition des commandes à l\'écran que vous avez sauvegardée?</string>
|
||||
<string name="toast_reset_osc_success">Les contrôles à l\'écran sont réinitialisés</string>
|
||||
<string name="title_osc_opacity">Modifier l\'opacité des contrôles à l\'écran</string>
|
||||
<string name="summary_osc_opacity">Rendre les contrôles à l\'écran plus/moins transparents</string>
|
||||
<string name="dialog_title_osc_opacity">Modifiez l\'opacité</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
|
||||
<string name="category_ui_settings">Paramètres de l\'interface utilisateur</string>
|
||||
<string name="title_language_list">Langue</string>
|
||||
|
||||
@@ -113,8 +113,6 @@
|
||||
<string name="summary_checkbox_enable_pip">Permette di osservare (ma non di controllare) la stream in multitasking</string>
|
||||
|
||||
<string name="category_audio_settings">Impostazioni audio</string>
|
||||
<string name="title_checkbox_51_surround">Abilita l\'audio 5.1 surround</string>
|
||||
<string name="summary_checkbox_51_surround">Se riscontri problemi, disabilitalo. Richiede GFE 2.7 o versioni sucessive.</string>
|
||||
|
||||
<string name="title_checkbox_multi_controller">Supporto a più controller</string>
|
||||
<string name="summary_checkbox_multi_controller">Quando disabilitato, tutti i controller appaiono come uno solo</string>
|
||||
@@ -159,5 +157,6 @@
|
||||
<string name="summary_video_format">H.265 riduce i requisiti di larghezza di banda video ma richiede un dispositivo molto recente</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="suffix_osc_opacity">%</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -90,8 +90,6 @@
|
||||
<string name="summary_checkbox_disable_warnings">ストリーミング中に画面に警告メッセージを表示しない</string>
|
||||
|
||||
<string name="category_audio_settings">音声</string>
|
||||
<string name="title_checkbox_51_surround">5.1chサラウンド</string>
|
||||
<string name="summary_checkbox_51_surround">音声に問題が生じる場合はチェックを外してください。バージョン2.7以降のGFEが必要です</string>
|
||||
|
||||
<string name="title_checkbox_multi_controller">複数のゲームコントローラ</string>
|
||||
<string name="summary_checkbox_multi_controller">チェックを外すと、全てのゲームコントローラが単一の物として認識されます</string>
|
||||
@@ -121,5 +119,18 @@
|
||||
<string name="category_advanced_settings">高度な設定</string>
|
||||
<string name="title_video_format">H.265</string>
|
||||
<string name="summary_video_format">H.265は動画に必要な帯域幅を圧縮します。この機能にはなるべく新しいデバイスが必要です</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
<string name="dialog_title_osc_opacity">透過率</string>
|
||||
<string name="title_osc_opacity">透過率</string>
|
||||
<string name="summary_osc_opacity">オンスクリーンコントローラの透過率を調整します</string>
|
||||
<string name="title_only_l3r3">L3 と R3 のみ表示します</string>
|
||||
<string name="summary_only_l3r3">L3 と R3 以外のボタンを表示しない</string>
|
||||
<string name="title_reset_osc">オンスクリーンコントローラをデフォルトに戻します</string>
|
||||
<string name="summary_reset_osc">サイズやレイアウトを戻します</string>
|
||||
<string name="dialog_title_reset_osc">デフォルトに戻します</string>
|
||||
<string name="dialog_text_reset_osc">本当にデフォルトに戻しますか?</string>
|
||||
<string name="toast_reset_osc_success">オンスクリーンコントローラをデフォルトに戻しました</string>
|
||||
<string name="title_checkbox_vibrate_osc">振動</string>
|
||||
<string name="summary_checkbox_vibrate_osc">コントローラの振動を真似します</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -105,8 +105,6 @@
|
||||
<string name="summary_checkbox_disable_warnings">화면 상의 연결 경고 메세지를 스트리밍 중에 비활성화합니다.</string>
|
||||
|
||||
<string name="category_audio_settings">오디오 설정</string>
|
||||
<string name="title_checkbox_51_surround">5.1 서라운드 사운드 활성화</string>
|
||||
<string name="summary_checkbox_51_surround">오디오 문제가 발생한다면 체크를 해제하세요. GFE 2.7이나 그 이상 버전이 필요합니다.</string>
|
||||
|
||||
<string name="title_checkbox_multi_controller">다중 컨트롤러 지원</string>
|
||||
<string name="summary_checkbox_multi_controller">이 옵션을 선택하지 않으면 모든 컨트롤러가 하나로 표시됩니다</string>
|
||||
@@ -136,5 +134,6 @@
|
||||
<string name="category_advanced_settings">고급 설정</string>
|
||||
<string name="title_video_format">H.265 설정 변경</string>
|
||||
<string name="summary_video_format">H.265는 비디오 대역폭 요구사항을 낮춰주지만 최신 장치가 필요합니다.</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -94,8 +94,6 @@
|
||||
<string name="summary_checkbox_disable_warnings">Verberg on-screen verbindingswaarschuwingen tijdens het streamen</string>
|
||||
|
||||
<string name="category_audio_settings">Geluidsinstellingen</string>
|
||||
<string name="title_checkbox_51_surround">Gebruik 5.1 surround sound</string>
|
||||
<string name="summary_checkbox_51_surround">Gebruik dit niet als er problemen zijn met de audio. Vereist GFE 2.7 of hoger.</string>
|
||||
|
||||
<string name="title_checkbox_multi_controller">Multi-gamepad support</string>
|
||||
<string name="summary_checkbox_multi_controller">Wanneer uitgevinkt, alle controllers verschijnen als één.</string>
|
||||
@@ -125,5 +123,6 @@
|
||||
<string name="category_advanced_settings">Geavanceerde Instellingen</string>
|
||||
<string name="title_video_format">Verander H.265 instellingen</string>
|
||||
<string name="summary_video_format">H.265 verlaagt video bandbreedte vereisten maar benodigdt een recent apparaat.</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -96,8 +96,6 @@
|
||||
<string name="summary_checkbox_disable_warnings">Выключить экранные предупреждения о соединении во время трансляции</string>
|
||||
|
||||
<string name="category_audio_settings">Аудио Настройки</string>
|
||||
<string name="title_checkbox_51_surround">Включить объёмный звук 5.1</string>
|
||||
<string name="summary_checkbox_51_surround">Отключите, если появляются аудио проблемы. Требуется GFE 2.7 или выше.</string>
|
||||
|
||||
<string name="title_checkbox_multi_controller">Поддержка нескольких контроллеров</string>
|
||||
<string name="summary_checkbox_multi_controller">Когда отключена, все контроллеры определяются как один</string>
|
||||
@@ -183,4 +181,5 @@
|
||||
<string name="summary_fps_list">Увеличение для более плавного видео потока. Уменьшите для лучшей производительности на более слабых устройствах.</string>
|
||||
<string name="scut_invalid_uuid">Указанный PC недействителен</string>
|
||||
<string name="scut_invalid_app_id">Указанное приложение недействительно</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_label" translatable="false">Moonlight</string>
|
||||
<string name="app_label_root" translatable="false">Moonlight (Root)</string>
|
||||
|
||||
<!-- Shortcut strings -->
|
||||
<string name="scut_deleted_pc"> 电脑已删除 </string>
|
||||
<string name="scut_not_paired"> 电脑没有配对成功 </string>
|
||||
<string name="scut_not_paired"> 电脑未配对 </string>
|
||||
<string name="scut_pc_not_found"> 电脑未找到 </string>
|
||||
<string name="scut_invalid_uuid"> 提供的电脑无效 </string>
|
||||
<string name="scut_invalid_app_id"> 提供的App无效 </string>
|
||||
|
||||
<!-- Help strings -->
|
||||
<string name="help_loading_title"> 帮助查看器 </string>
|
||||
<string name="help_loading_msg"> 加载帮助页面中…… </string>
|
||||
<string name="help_loading_title"> 查看帮助 </string>
|
||||
<string name="help_loading_msg"> 正在加载帮助页面…… </string>
|
||||
|
||||
<!-- PC view menu entries -->
|
||||
<string name="pcview_menu_app_list"> 浏览游戏列表 </string>
|
||||
@@ -14,23 +20,24 @@
|
||||
<string name="pcview_menu_unpair_pc"> 取消配对 </string>
|
||||
<string name="pcview_menu_send_wol"> 发送 Wake-On-LAN 请求 </string>
|
||||
<string name="pcview_menu_delete_pc"> 删除电脑 </string>
|
||||
<string name="pcview_menu_details"> 查看详情 </string>
|
||||
|
||||
<!-- Pair messages -->
|
||||
<string name="pairing"> 配对中…… </string>
|
||||
<string name="pair_pc_offline"> 电脑离线中 </string>
|
||||
<string name="pair_pc_ingame"> 电脑正在游戏中,你必须在配对之前先退出游戏 </string>
|
||||
<string name="pair_pc_ingame"> 电脑正在游戏中,在配对之前你必须先退出游戏 </string>
|
||||
<string name="pair_pairing_title"> 配对中 </string>
|
||||
<string name="pair_pairing_msg"> 请在目标电脑上输入以下的PIN码: </string>
|
||||
<string name="pair_pairing_msg"> 请在目标电脑上输入以下PIN码: </string>
|
||||
<string name="pair_incorrect_pin"> PIN码错误 </string>
|
||||
<string name="pair_fail"> 配对失败 </string>
|
||||
<string name="pair_already_in_progress"> 已经在配对中,请稍等 </string>
|
||||
<string name="pair_already_in_progress"> 配对中,请稍候 </string>
|
||||
|
||||
<!-- WOL messages -->
|
||||
<string name="wol_pc_online"> 电脑在线中 </string>
|
||||
<string name="wol_no_mac"> 由于GeForce Experience没有发送MAC地址过来因此无法唤醒电脑 </string>
|
||||
<string name="wol_no_mac"> 无法唤醒电脑因为GFE没有返回MAC地址 </string>
|
||||
<string name="wol_waking_pc"> 唤醒电脑中…… </string>
|
||||
<string name="wol_waking_msg"> 远程唤醒电脑需要一些时间\n\n
|
||||
如果电脑没有唤醒,请确保Wake-On-LAN的设置无误
|
||||
<string name="wol_waking_msg"> 唤醒电脑需要一些时间\n
|
||||
如果电脑没有唤醒,请确保Wake-On-LAN设置无误
|
||||
</string>
|
||||
<string name="wol_fail"> 无法发送Wake-On-LAN数据包 </string>
|
||||
|
||||
@@ -42,40 +49,54 @@
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_pc_offline"> 电脑离线中 </string>
|
||||
<string name="error_manager_not_running">ComputerManager服务不在运行中\n\n请稍等几秒或者直接重启Moonlight</string>
|
||||
<string name="error_unknown_host"> 无法解析电脑地址 </string>
|
||||
<string name="error_404"> GeForce Experience返回了HTTP 404 错误。确保你的显卡支持GAMESTREAM\n\n
|
||||
使用远程控制软件同样会引起此错误,请尝试重启电脑或者重新安装GeForce Experience
|
||||
<string name="error_manager_not_running"> ComputerManager服务未运行。\n请稍等几秒或重启App </string>
|
||||
<string name="error_unknown_host"> 无法解析主机地址 </string>
|
||||
<string name="error_404"> GFE返回了HTTP 404 错误,确保你的电脑显卡支持串流。\n
|
||||
使用远程桌面软件同样会引起此错误,请尝试重启电脑或重装GFE
|
||||
</string>
|
||||
<string name="title_decoding_error"> 视频解码器崩溃 </string>
|
||||
<string name="message_decoding_error"> 由于与该设备的视频解码器不兼容,Moonlight已崩溃。确保你电脑上的GFE已更新至最新版本,如果崩溃继续,请尝试调整串流设置。 </string>
|
||||
<string name="title_decoding_reset"> 重置视频设置 </string>
|
||||
<string name="message_decoding_reset"> 由于设备的视频解码器在你选择的串流设置上持续崩溃,已重置你的串流设置。 </string>
|
||||
<string name="error_usb_prohibited"> 设备管理员已禁止USB访问。请检查您的Knox或MDM设置。 </string>
|
||||
<string name="unable_to_pin_shortcut"> 您当前的桌面启动器不允许创建长按子菜单。 </string>
|
||||
|
||||
<!-- Start application messages -->
|
||||
<string name="conn_establishing_title"> 建立连接中 </string>
|
||||
<string name="conn_establishing_msg"> 启动连接中 </string>
|
||||
<string name="conn_metered"> 警告: 你正在使用移动网络,继续使用将会产生大量流量费用! </string>
|
||||
<string name="conn_client_latency"> 每帧解码平均延迟:</string>
|
||||
<string name="conn_client_latency_hw"> 硬解码器延迟:</string>
|
||||
<string name="conn_hardware_latency"> 硬解码器平均延迟:</string>
|
||||
<string name="conn_metered"> 警告:你正在使用移动网络连接! </string>
|
||||
<string name="conn_client_latency"> 平均每帧解码延迟:</string>
|
||||
<string name="conn_client_latency_hw"> 硬件解码器延迟:</string>
|
||||
<string name="conn_hardware_latency"> 硬件解码器平均延迟:</string>
|
||||
<string name="conn_starting"> 启动中…… </string>
|
||||
<string name="conn_error_title"> 连接错误 </string>
|
||||
<string name="conn_error_msg"> 启动失败 </string>
|
||||
<string name="conn_terminated_title"> 连接被终结 </string>
|
||||
<string name="conn_terminated_title"> 连接终结 </string>
|
||||
<string name="conn_terminated_msg"> 连接已被终结 </string>
|
||||
|
||||
<!-- General strings -->
|
||||
<string name="ip_hint"> 串流电脑的IP地址 </string>
|
||||
<string name="searching_pc"> 正在搜寻运行着GAMESTREAM的电脑…… \n\n
|
||||
确保GeForce Experience里面SHIELD选项里面的GAMESTREAM开关是开着的 </string>
|
||||
<string name="yes"> 是 </string>
|
||||
<string name="no"> 否 </string>
|
||||
<string name="lost_connection"> 失去了与电脑的连接 </string>
|
||||
<string name="help">帮助</string>
|
||||
<string name="searching_pc"> 正在搜寻运行GAMESTREAM的电脑…… \n
|
||||
请确保GFE SHIELD设置里的GAMESTREAM已开启。 </string>
|
||||
<string name="yes"> 确定 </string>
|
||||
<string name="no"> 取消 </string>
|
||||
<string name="lost_connection"> 与电脑失去连接 </string>
|
||||
<string name="title_details"> 详情 </string>
|
||||
<string name="help"> 帮助 </string>
|
||||
<string name="delete_pc_msg"> 你确定要删除这台电脑? </string>
|
||||
<string name="slow_connection_msg"> 与电脑连接过慢 \n 请降低码率 </string>
|
||||
<string name="poor_connection_msg"> 与电脑连接不良 </string>
|
||||
<string name="perf_overlay_text"> 视频分辨率: %1$s\n解码器: %2$s\n估计主机帧数: %3$.2f FPS\n网络接收帧数: %4$.2f FPS\n渲染帧数: %5$.2f FPS\n网络丢失帧: %6$.2f%%\n平均接收时间: %7$.2f ms\n平均解码时间: %8$.2f ms </string>
|
||||
|
||||
<!-- AppList activity -->
|
||||
<string name="applist_connect_msg"> 连接到电脑中…… </string>
|
||||
<string name="applist_connect_msg"> 正在连接电脑…… </string>
|
||||
<string name="applist_menu_resume"> 恢复串流 </string>
|
||||
<string name="applist_menu_quit"> 退出串流 </string>
|
||||
<string name="applist_menu_quit_and_start"> 退出当前游戏并开始这个游戏 </string>
|
||||
<string name="applist_menu_cancel"> 取消 </string>
|
||||
<string name="applist_menu_details"> 查看详情 </string>
|
||||
<string name="applist_menu_scut"> 创建快捷方式 </string>
|
||||
<string name="applist_menu_tv_channel"> 添加到频道 </string>
|
||||
<string name="applist_refresh_title"> 游戏列表 </string>
|
||||
<string name="applist_refresh_msg"> 刷新中…… </string>
|
||||
<string name="applist_refresh_error_title"> 错误 </string>
|
||||
@@ -83,81 +104,109 @@
|
||||
<string name="applist_quit_app"> 退出中 </string>
|
||||
<string name="applist_quit_success"> 成功退出串流 </string>
|
||||
<string name="applist_quit_fail"> 退出串流失败 </string>
|
||||
<string name="applist_quit_confirmation"> 您确定要退出当前游戏?\n\n所有未保存的数据将丢失 </string>
|
||||
<string name="applist_quit_confirmation"> 您确定要退出当前游戏?\n所有未保存的数据将丢失。 </string>
|
||||
<string name="applist_details_id">App ID:</string>
|
||||
|
||||
<!-- Add computer manually activity -->
|
||||
<string name="title_add_pc"> 手动添加电脑 </string>
|
||||
<string name="msg_add_pc"> 连接到电脑中…… </string>
|
||||
<string name="addpc_fail"> 无法连接到指定的电脑。请确保指定的端口没有被防火墙阻止 </string>
|
||||
<string name="msg_add_pc"> 正在连接电脑…… </string>
|
||||
<string name="addpc_fail"> 无法连接至指定电脑。请确保所需端口没有被防火墙阻止 </string>
|
||||
<string name="addpc_success"> 成功添加电脑 </string>
|
||||
<string name="addpc_unknown_host"> 无法解析电脑的IP地址,请确保IP地址输入无误 </string>
|
||||
<string name="addpc_enter_ip"> 请输入一个IP地址! </string>
|
||||
<string name="addpc_wrong_sitelocal"> 该地址似乎不正确。 您必须使用路由器的公共IP地址通过Internet进行串流。 </string>
|
||||
|
||||
<!-- Preferences -->
|
||||
<string name="category_basic_settings"> 基本设置 </string>
|
||||
<string name="title_resolution_list"> 选择目标分辨率和帧数 </string>
|
||||
<string name="summary_resolution_list"> 过高的设定会引起串流卡顿甚至软件闪退 </string>
|
||||
<string name="title_seekbar_bitrate"> 选择目标视频码率 </string>
|
||||
<string name="summary_seekbar_bitrate"> 低码率减少卡顿,高码率提高画质 </string>
|
||||
<string name="title_resolution_list"> 视频分辨率 </string>
|
||||
<string name="summary_resolution_list"> 高分辨率提升图像清晰度。 \n 低分辨率提升在低端设备和较慢网络中的串流体验。 </string>
|
||||
<string name="title_fps_list"> 视频帧数 </string>
|
||||
<string name="summary_fps_list"> 高帧数提升视频流流畅度。 \n 低帧数提升在低端设备中的串流体验。</string>
|
||||
<string name="title_seekbar_bitrate"> 视频码率 </string>
|
||||
<string name="summary_seekbar_bitrate"> 高码率提升图像质量。 \n 低码率提升在较慢网络中的串流体验。 </string>
|
||||
<string name="suffix_seekbar_bitrate">Kbps</string>
|
||||
<string name="title_unlock_fps"> 解锁所有可用帧数 </string>
|
||||
<string name="summary_unlock_fps"> 以90或120帧串流可能会减少在高端设备上的网络延迟,但会在不支持的设备上造成卡顿或崩溃。 </string>
|
||||
<string name="title_checkbox_stretch_video"> 将画面拉伸至全屏 </string>
|
||||
<string name="title_checkbox_disable_warnings"> 禁用错误提示 </string>
|
||||
<string name="summary_checkbox_disable_warnings"> 串流过程中禁用连接错误提示 </string>
|
||||
<string name="summary_checkbox_disable_warnings"> 串流中禁用连接错误提示 </string>
|
||||
<string name="title_checkbox_enable_pip">启用画中画观察模式</string>
|
||||
<string name="summary_checkbox_enable_pip">允许多任务时观看串流画面(但不操作)</string>
|
||||
|
||||
<string name="category_audio_settings"> 音频设置 </string>
|
||||
<string name="title_checkbox_51_surround"> 启用 5.1 环绕音效 </string>
|
||||
<string name="summary_checkbox_51_surround"> 如果你的声音听起来有问题请禁用。\n\n需要GeForce Experience 2.7 或更高版本 </string>
|
||||
|
||||
<string name="title_checkbox_multi_controller"> 启用多手柄支持 </string>
|
||||
<string name="summary_checkbox_multi_controller"> 如果禁用,所有手柄将会认作一个手柄 </string>
|
||||
<string name="category_input_settings">输入设置</string>
|
||||
<string name="title_checkbox_multi_controller"> 自动检测手柄 </string>
|
||||
<string name="summary_checkbox_multi_controller"> 禁用此项所有手柄将视为一个手柄 </string>
|
||||
<string name="title_checkbox_vibrate_fallback"> 用设备震动模拟游戏震动效果 </string>
|
||||
<string name="summary_checkbox_vibrate_fallback"> 如果你的手柄不支持震动,则震动设备以模拟游戏震动效果 </string>
|
||||
<string name="title_seekbar_deadzone"> 调整摇杆死区 </string>
|
||||
<string name="suffix_seekbar_deadzone">%</string>
|
||||
<string name="title_checkbox_xb1_driver">Xbox 360/One 手柄驱动 </string>
|
||||
<string name="summary_checkbox_xb1_driver"> 若要在那些没有原生Xbox手柄驱动的设备上使用Xbox手柄,请勾上此复选框 </string>
|
||||
<string name="summary_checkbox_xb1_driver"> 为缺少原生Xbox手柄支持的设备启用内置USB驱动 </string>
|
||||
<string name="title_checkbox_usb_bind_all"> 覆盖安卓手柄支持 </string>
|
||||
<string name="summary_checkbox_usb_bind_all"> 强制Moonlight的USB驱动接管所有受支持的Xbox手柄 </string>
|
||||
<string name="title_checkbox_mouse_emulation"> 通过手柄模拟鼠标 </string>
|
||||
<string name="summary_checkbox_mouse_emulation"> 长按开始键将手柄切换为鼠标模式 </string>
|
||||
<string name="title_checkbox_mouse_nav_buttons"> 启用前进后退鼠标键 </string>
|
||||
<string name="summary_checkbox_mouse_nav_buttons"> 在一些支持不佳的设备上启用此项可能会使其右键失效 </string>
|
||||
|
||||
<string name="category_on_screen_controls_settings"> 触屏设置 </string>
|
||||
<string name="title_checkbox_show_onscreen_controls"> 启用虚拟手柄 </string>
|
||||
<string name="summary_checkbox_show_onscreen_controls"> 将在串流画面上显示一层虚拟手柄 </string>
|
||||
<string name="category_on_screen_controls_settings"> 屏幕控制按钮设置 </string>
|
||||
<string name="title_checkbox_show_onscreen_controls"> 显示屏幕控制按钮 </string>
|
||||
<string name="summary_checkbox_show_onscreen_controls"> 在触摸屏上显示一层虚拟手柄 </string>
|
||||
<string name="title_checkbox_vibrate_osc"> 启用震动 </string>
|
||||
<string name="summary_checkbox_vibrate_osc"> 使用屏幕控制按钮时震动设备以模拟游戏震动效果 </string>
|
||||
<string name="title_only_l3r3"> 只显示L3和R3 </string>
|
||||
<string name="summary_only_l3r3"> 隐藏除L3和R3外的所有虚拟按钮 </string>
|
||||
<string name="title_reset_osc"> 重置已保存的屏幕控制按钮布局 </string>
|
||||
<string name="summary_reset_osc"> 重置所有屏幕控制按钮为默认大小和位置 </string>
|
||||
<string name="dialog_title_reset_osc"> 重置按钮布局 </string>
|
||||
<string name="dialog_text_reset_osc"> 你确定要删除所保存的屏幕按钮布局吗? </string>
|
||||
<string name="toast_reset_osc_success"> 屏幕按钮布局已经重置 </string>
|
||||
|
||||
<string name="category_ui_settings"> 界面设置 </string>
|
||||
<string name="title_language_list">语言</string>
|
||||
<string name="summary_language_list"> 请选择您希望Moonlight使用的语言 </string>
|
||||
<string name="title_language_list"> 语言 </string>
|
||||
<string name="summary_language_list"> 选择Moonlight显示的语言 </string>
|
||||
<string name="title_checkbox_list_mode"> 使用列表代替图标 </string>
|
||||
<string name="summary_checkbox_list_mode"> 将以列表来显示电脑和游戏 </string>
|
||||
<string name="summary_checkbox_list_mode"> 列表显示电脑和游戏 </string>
|
||||
<string name="title_checkbox_small_icon_mode"> 使用小图标 </string>
|
||||
<string name="summary_checkbox_small_icon_mode"> 使用小图标以在屏幕上显示更多项目 </string>
|
||||
|
||||
<string name="category_host_settings"> 主机设置 </string>
|
||||
<string name="title_checkbox_enable_sops"> 优化游戏设置 </string>
|
||||
<string name="summary_checkbox_enable_sops"> 允许GeForce Experience为最佳串流效果自动更改游戏设置 </string>
|
||||
<string name="title_checkbox_host_audio"> 将声音输出到电脑上 </string>
|
||||
<string name="summary_checkbox_host_audio"> 将在电脑和本设备同时输出声音 </string>
|
||||
<string name="summary_checkbox_enable_sops"> 允许GFE为最佳串流效果自动更改游戏设置 </string>
|
||||
<string name="title_checkbox_host_audio"> 在电脑上播放声音 </string>
|
||||
<string name="summary_checkbox_host_audio"> 在电脑和本设备同时输出声音 </string>
|
||||
|
||||
<string name="category_advanced_settings"> 高级设置 </string>
|
||||
<string name="title_video_format"> H.265设置 </string>
|
||||
<string name="summary_video_format">H.265能降低带宽需求,但是需要设备支持 </string>
|
||||
<string name="applist_menu_scut">创建快捷方式</string>
|
||||
<string name="category_input_settings">输入设置</string>
|
||||
<string name="applist_menu_details">查看详情</string>
|
||||
<string name="dialog_text_reset_osc">你确定要删除所保存的按钮布局吗?</string>
|
||||
<string name="dialog_title_reset_osc">重置按钮布局</string>
|
||||
<string name="delete_pc_msg">你确定要删除这台电脑?</string>
|
||||
<string name="pcview_menu_details">查看详情</string>
|
||||
<string name="scut_pc_not_found">电脑无法找到</string>
|
||||
<string name="toast_reset_osc_success">按钮布局已经重置</string>
|
||||
<string name="title_fps_list">视频帧数</string>
|
||||
<string name="title_unlock_fps">解锁所有可用帧数</string>
|
||||
<string name="title_only_l3r3">只显示[L3]和[R3]</string>
|
||||
<string name="title_reset_osc">重置已经保存的触摸按钮布局</string>
|
||||
<string name="title_disable_frame_drop">永不掉帧</string>
|
||||
<string name="title_enable_hdr">启用 HDR (实验)</string>
|
||||
<string name="title_checkbox_vibrate_osc">启动震动</string>
|
||||
<string name="title_details">详情</string>
|
||||
<string name="title_decoding_reset">重置视频设置</string>
|
||||
<string name="title_checkbox_mouse_emulation">通过手柄模拟鼠标</string>
|
||||
<string name="summary_only_l3r3">隐藏所有虚拟按钮除了L3和R3</string>
|
||||
<string name="title_enable_perf_overlay">启用性能信息</string>
|
||||
<string name="summary_enable_perf_overlay">在串流中显示实时性能信息</string>
|
||||
<string name="perf_overlay_text">视频分辨率: %1$s\n解码器: %2$s\n估计主机帧数: %3$.2f FPS\n网络接收帧数: %4$.2f FPS\n渲染帧数: %5$.2f FPS\n网络丢失帧: %6$.2f%%\n平均接收时间: %7$.2f ms\n平均解码时间: %8$.2f ms</string>
|
||||
<string name="title_disable_frame_drop"> 永不掉帧 </string>
|
||||
<string name="summary_disable_frame_drop"> 可能会减少在一些设备上的卡顿,但会增加延迟 </string>
|
||||
<string name="title_video_format"> 更改H.265设置 </string>
|
||||
<string name="summary_video_format">H.265能降低视频带宽需求,但需要较新的设备才能支持</string>
|
||||
<string name="title_enable_hdr"> 启用 HDR (实验) </string>
|
||||
<string name="summary_enable_hdr"> 当游戏和显卡支持时以HDR模式串流。 HDR需要GTX 1000系列或更高规格显卡。 </string>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<string name="title_enable_perf_overlay"> 启用性能信息 </string>
|
||||
<string name="summary_enable_perf_overlay"> 在串流中显示实时性能信息 </string>
|
||||
<string name="title_osc_opacity">更改屏幕按钮透明度</string>
|
||||
<string name="dialog_title_osc_opacity">透明度</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
<string name="summary_osc_opacity">令屏幕按钮变得更透明/更不透明</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_label" translatable="false">Moonlight</string>
|
||||
<string name="app_label_root" translatable="false">Moonlight (Root)</string>
|
||||
|
||||
<!-- Shortcut strings -->
|
||||
<string name="scut_deleted_pc"> 電腦已刪除 </string>
|
||||
<string name="scut_not_paired"> 電腦沒有配對成功 </string>
|
||||
<string name="scut_not_paired"> 電腦未配對 </string>
|
||||
<string name="scut_pc_not_found"> 電腦未找到 </string>
|
||||
<string name="scut_invalid_uuid"> 提供的電腦無效 </string>
|
||||
<string name="scut_invalid_app_id"> 提供的App無效 </string>
|
||||
|
||||
<!-- Help strings -->
|
||||
<string name="help_loading_title"> 幫助查看器 </string>
|
||||
<string name="help_loading_msg"> 載入幫助頁面中…… </string>
|
||||
<string name="help_loading_title"> 查看幫助 </string>
|
||||
<string name="help_loading_msg"> 正在載入説明頁面…… </string>
|
||||
|
||||
<!-- PC view menu entries -->
|
||||
<string name="pcview_menu_app_list"> 瀏覽遊戲列表 </string>
|
||||
<string name="pcview_menu_app_list"> 流覽遊戲列表 </string>
|
||||
<string name="pcview_menu_pair_pc"> 和電腦配對 </string>
|
||||
<string name="pcview_menu_unpair_pc"> 取消配對 </string>
|
||||
<string name="pcview_menu_send_wol"> 發送 Wake-On-LAN 請求 </string>
|
||||
<string name="pcview_menu_delete_pc"> 刪除電腦 </string>
|
||||
<string name="pcview_menu_details"> 查看詳情 </string>
|
||||
|
||||
<!-- Pair messages -->
|
||||
<string name="pairing"> 配對中…… </string>
|
||||
<string name="pair_pc_offline"> 電腦離線中 </string>
|
||||
<string name="pair_pc_ingame"> 電腦正在遊戲中,你必須在配對之前先退出遊戲 </string>
|
||||
<string name="pair_pc_ingame"> 電腦正在遊戲中,在配對之前你必須先退出遊戲 </string>
|
||||
<string name="pair_pairing_title"> 配對中 </string>
|
||||
<string name="pair_pairing_msg"> 請在目標電腦上輸入以下的PIN碼: </string>
|
||||
<string name="pair_pairing_msg"> 請在目標電腦上輸入以下PIN碼: </string>
|
||||
<string name="pair_incorrect_pin"> PIN碼錯誤 </string>
|
||||
<string name="pair_fail"> 配對失敗 </string>
|
||||
<string name="pair_already_in_progress"> 已經在配對中,請稍等 </string>
|
||||
<string name="pair_already_in_progress"> 配對中,請稍候 </string>
|
||||
|
||||
<!-- WOL messages -->
|
||||
<string name="wol_pc_online"> 電腦在線中 </string>
|
||||
<string name="wol_no_mac"> 由於GeForce Experience沒有發送MAC地址過來因此無法喚醒電腦 </string>
|
||||
<string name="wol_pc_online"> 電腦線上中 </string>
|
||||
<string name="wol_no_mac"> 無法喚醒電腦因為GFE沒有返回MAC地址 </string>
|
||||
<string name="wol_waking_pc"> 喚醒電腦中…… </string>
|
||||
<string name="wol_waking_msg"> 遠程喚醒電腦需要一些時間\n\n
|
||||
如果電腦沒有喚醒,請確保Wake-On-LAN的設置無誤
|
||||
<string name="wol_waking_msg"> 喚醒電腦需要一些時間\n
|
||||
如果電腦沒有喚醒,請確保Wake-On-LAN設置無誤
|
||||
</string>
|
||||
<string name="wol_fail"> 無法發送Wake-On-LAN數據包 </string>
|
||||
<string name="wol_fail"> 無法發送Wake-On-LAN資料包 </string>
|
||||
|
||||
<!-- Unpair messages -->
|
||||
<string name="unpairing"> 取消配對中…… </string>
|
||||
@@ -42,40 +49,54 @@
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_pc_offline"> 電腦離線中 </string>
|
||||
<string name="error_manager_not_running">ComputerManager服務不在運行中\n\n請稍等幾秒或者直接重啟Moonlight</string>
|
||||
<string name="error_unknown_host"> 無法解析電腦地址 </string>
|
||||
<string name="error_404"> GeForce Experience返回了HTTP 404 錯誤。確保你的顯卡支持GAMESTREAM\n\n
|
||||
使用遠程控制軟體同樣會引起此錯誤,請嘗試重啟電腦或者重新安裝GeForce Experience
|
||||
<string name="error_manager_not_running"> ComputerManager服務未運行。\n請稍等幾秒或重啟App </string>
|
||||
<string name="error_unknown_host"> 無法解析主機位址 </string>
|
||||
<string name="error_404"> GFE返回了HTTP 404 錯誤,確保你的電腦顯卡支援串流。\n
|
||||
使用遠端桌面軟體同樣會引起此錯誤,請嘗試重啟電腦或重裝GFE
|
||||
</string>
|
||||
<string name="title_decoding_error"> 視頻解碼器崩潰 </string>
|
||||
<string name="message_decoding_error"> 由於與該設備的視頻解碼器不相容,Moonlight已崩潰。確保你電腦上的GFE已更新至最新版本,如果崩潰繼續,請嘗試調整串流設置。 </string>
|
||||
<string name="title_decoding_reset"> 重置視頻設置 </string>
|
||||
<string name="message_decoding_reset"> 由於設備的視頻解碼器在你選擇的串流設置上持續崩潰,已重置你的串流設置。 </string>
|
||||
<string name="error_usb_prohibited"> 設備管理員已禁止USB訪問。請檢查您的Knox或MDM設置。 </string>
|
||||
<string name="unable_to_pin_shortcut"> 您當前的桌面啟動器不允許創建長按子功能表。 </string>
|
||||
|
||||
<!-- Start application messages -->
|
||||
<string name="conn_establishing_title"> 建立連接中 </string>
|
||||
<string name="conn_establishing_msg"> 啟動連接中 </string>
|
||||
<string name="conn_metered"> 警告: 你正在使用移動網路,繼續使用將會產生大量流量費用! </string>
|
||||
<string name="conn_client_latency"> 每幀解碼平均延遲:</string>
|
||||
<string name="conn_client_latency_hw"> 硬解碼器延遲:</string>
|
||||
<string name="conn_hardware_latency"> 硬解碼器平均延遲:</string>
|
||||
<string name="conn_metered"> 警告:你正在使用移動網路連接! </string>
|
||||
<string name="conn_client_latency"> 平均每幀解碼延遲:</string>
|
||||
<string name="conn_client_latency_hw"> 硬體解碼器延遲:</string>
|
||||
<string name="conn_hardware_latency"> 硬體解碼器平均延遲:</string>
|
||||
<string name="conn_starting"> 啟動中…… </string>
|
||||
<string name="conn_error_title"> 連接錯誤 </string>
|
||||
<string name="conn_error_msg"> 啟動失敗 </string>
|
||||
<string name="conn_terminated_title"> 連接被終結 </string>
|
||||
<string name="conn_terminated_title"> 連接終結 </string>
|
||||
<string name="conn_terminated_msg"> 連接已被終結 </string>
|
||||
|
||||
<!-- General strings -->
|
||||
<string name="ip_hint"> 串流電腦的IP地址 </string>
|
||||
<string name="searching_pc"> 正在搜尋運行著GAMESTREAM的電腦…… \n\n
|
||||
確保GeForce Experience裡面SHIELD選項裡面的GAMESTREAM開關是開著的 </string>
|
||||
<string name="yes"> 是 </string>
|
||||
<string name="no"> 否 </string>
|
||||
<string name="lost_connection"> 失去了與電腦的連接 </string>
|
||||
<string name="help">幫助</string>
|
||||
<string name="searching_pc"> 正在搜尋運行GAMESTREAM的電腦…… \n
|
||||
請確保GFE SHIELD設置裡的GAMESTREAM已開啟。 </string>
|
||||
<string name="yes"> 確定 </string>
|
||||
<string name="no"> 取消 </string>
|
||||
<string name="lost_connection"> 與電腦失去連接 </string>
|
||||
<string name="title_details"> 詳情 </string>
|
||||
<string name="help"> 幫助 </string>
|
||||
<string name="delete_pc_msg"> 你確定要刪除這台電腦? </string>
|
||||
<string name="slow_connection_msg"> 與電腦連接過慢 \n 請降低碼率 </string>
|
||||
<string name="poor_connection_msg"> 與電腦連接不良 </string>
|
||||
<string name="perf_overlay_text"> 視頻解析度: %1$s\n解碼器: %2$s\n估計主機幀數: %3$.2f FPS\n網路接收幀數: %4$.2f FPS\n渲染幀數: %5$.2f FPS\n網路丟失幀: %6$.2f%%\n平均接收時間: %7$.2f ms\n平均解碼時間: %8$.2f ms </string>
|
||||
|
||||
<!-- AppList activity -->
|
||||
<string name="applist_connect_msg"> 連接到電腦中…… </string>
|
||||
<string name="applist_connect_msg"> 正在連接電腦…… </string>
|
||||
<string name="applist_menu_resume"> 恢復串流 </string>
|
||||
<string name="applist_menu_quit"> 退出串流 </string>
|
||||
<string name="applist_menu_quit_and_start"> 退出當前遊戲並開始這個遊戲 </string>
|
||||
<string name="applist_menu_cancel"> 取消 </string>
|
||||
<string name="applist_menu_details"> 查看詳情 </string>
|
||||
<string name="applist_menu_scut"> 創建快捷方式 </string>
|
||||
<string name="applist_menu_tv_channel"> 添加到頻道 </string>
|
||||
<string name="applist_refresh_title"> 遊戲列表 </string>
|
||||
<string name="applist_refresh_msg"> 刷新中…… </string>
|
||||
<string name="applist_refresh_error_title"> 錯誤 </string>
|
||||
@@ -83,85 +104,109 @@
|
||||
<string name="applist_quit_app"> 退出中 </string>
|
||||
<string name="applist_quit_success"> 成功退出串流 </string>
|
||||
<string name="applist_quit_fail"> 退出串流失敗 </string>
|
||||
<string name="applist_quit_confirmation"> 您確定要退出當前遊戲?\n\n所有未保存的數據將丟失 </string>
|
||||
<string name="applist_quit_confirmation"> 您確定要退出當前遊戲?\n所有未保存的資料將丟失。 </string>
|
||||
<string name="applist_details_id">App ID:</string>
|
||||
|
||||
<!-- Add computer manually activity -->
|
||||
<string name="title_add_pc"> 手動添加電腦 </string>
|
||||
<string name="msg_add_pc"> 連接到電腦中…… </string>
|
||||
<string name="addpc_fail"> 無法連接到指定的電腦。請確保指定的埠沒有被防火牆阻止 </string>
|
||||
<string name="msg_add_pc"> 正在連接電腦…… </string>
|
||||
<string name="addpc_fail"> 無法連接至指定電腦。請確保所需埠沒有被防火牆阻止 </string>
|
||||
<string name="addpc_success"> 成功添加電腦 </string>
|
||||
<string name="addpc_unknown_host"> 無法解析電腦的IP地址,請確保IP地址輸入無誤 </string>
|
||||
<string name="addpc_enter_ip"> 請輸入一個IP地址! </string>
|
||||
<string name="addpc_unknown_host"> 無法解析電腦的IP位址,請確保IP位址輸入無誤 </string>
|
||||
<string name="addpc_enter_ip"> 請輸入一個IP位址! </string>
|
||||
<string name="addpc_wrong_sitelocal"> 該位址似乎不正確。 您必須使用路由器的公共IP位址通過Internet進行串流。 </string>
|
||||
|
||||
<!-- Preferences -->
|
||||
<string name="category_basic_settings"> 基本設置 </string>
|
||||
<string name="title_resolution_list"> 選擇目標解析度和幀數 </string>
|
||||
<string name="summary_resolution_list"> 過高的設定會引起串流卡頓甚至軟體閃退 </string>
|
||||
<string name="title_fps_list">影像幀數</string>
|
||||
<string name="summary_fps_list">增加以提供更流暢的影像串流. 減少以在較低的配備上獲得較好的效能.</string>
|
||||
<string name="title_seekbar_bitrate"> 選擇目標影像碼率 </string>
|
||||
<string name="summary_seekbar_bitrate"> 低碼率減少卡頓,高碼率提高畫質 </string>
|
||||
<string name="title_resolution_list"> 視頻解析度 </string>
|
||||
<string name="summary_resolution_list"> 高解析度提升圖像清晰度。 \n 低解析度提升在低端設備和較慢網路中的串流體驗。 </string>
|
||||
<string name="title_fps_list"> 視頻幀數 </string>
|
||||
<string name="summary_fps_list"> 高幀數提升視頻流流暢度。 \n 低幀數提升在低端設備中的串流體驗。</string>
|
||||
<string name="title_seekbar_bitrate"> 視頻碼率 </string>
|
||||
<string name="summary_seekbar_bitrate"> 高碼率提升圖像品質。 \n 低碼率提升在較慢網路中的串流體驗。 </string>
|
||||
<string name="suffix_seekbar_bitrate">Kbps</string>
|
||||
<string name="title_unlock_fps">解鎖所有可使用的幀率</string>
|
||||
<string name="summary_unlock_fps">串流於90或120幀下可以減少高級配備的延遲, 但可能導致無法支持的配備出現延遲或崩潰</string>
|
||||
<string name="title_unlock_fps"> 解鎖所有可用幀數 </string>
|
||||
<string name="summary_unlock_fps"> 以90或120幀串流可能會減少在高端設備上的網路延遲,但會在不支援的設備上造成卡頓或崩潰。 </string>
|
||||
<string name="title_checkbox_stretch_video"> 將畫面拉伸至全屏 </string>
|
||||
<string name="title_checkbox_disable_warnings"> 禁用錯誤提示 </string>
|
||||
<string name="summary_checkbox_disable_warnings"> 串流過程中禁用連接錯誤提示 </string>
|
||||
<string name="title_checkbox_enable_pip">啟用子母畫面</string>
|
||||
<string name="summary_checkbox_enable_pip">當多工處理時, 允許觀看串流畫面(無法控制)</string>
|
||||
<string name="summary_checkbox_disable_warnings"> 串流中禁用連接錯誤提示 </string>
|
||||
<string name="title_checkbox_enable_pip">啟用畫中畫觀察模式</string>
|
||||
<string name="summary_checkbox_enable_pip">允許多工時觀看串流畫面(但不操作)</string>
|
||||
|
||||
<string name="category_audio_settings"> 音頻設置 </string>
|
||||
<string name="title_checkbox_51_surround"> 啟用 5.1 環繞音效 </string>
|
||||
<string name="summary_checkbox_51_surround"> 如果你的聲音聽起來有問題請禁用。\n\n需要GeForce Experience 2.7 或更高版本 </string>
|
||||
<string name="category_audio_settings"> 音訊設置 </string>
|
||||
|
||||
<string name="category_input_settings">輸入設定</string>
|
||||
<string name="title_checkbox_multi_controller"> 啟用多手柄支持 </string>
|
||||
<string name="summary_checkbox_multi_controller"> 如果禁用,所有手柄將會認作一個手柄 </string>
|
||||
<string name="title_checkbox_vibrate_fallback">以手機模擬手柄震動</string>
|
||||
<string name="summary_checkbox_vibrate_fallback">當手柄不支援震動時, 以手機來模擬</string>
|
||||
<string name="title_seekbar_deadzone"> 調整手柄死區 </string>
|
||||
<string name="category_input_settings">輸入設置</string>
|
||||
<string name="title_checkbox_multi_controller"> 自動檢測手柄 </string>
|
||||
<string name="summary_checkbox_multi_controller"> 禁用此項所有手柄將視為一個手柄 </string>
|
||||
<string name="title_checkbox_vibrate_fallback"> 用設備震動類比遊戲震動效果 </string>
|
||||
<string name="summary_checkbox_vibrate_fallback"> 如果你的手柄不支援震動,則震動設備以類比遊戲震動效果 </string>
|
||||
<string name="title_seekbar_deadzone"> 調整搖杆死區 </string>
|
||||
<string name="suffix_seekbar_deadzone">%</string>
|
||||
<string name="title_checkbox_xb1_driver">Xbox 360/One 手柄驅動 </string>
|
||||
<string name="summary_checkbox_xb1_driver"> 若要在那些沒有原生Xbox手柄驅動的設備上使用Xbox手柄,請勾上此複選框 </string>
|
||||
<string name="title_checkbox_usb_bind_all">覆蓋Android控制器支援</string>
|
||||
<string name="summary_checkbox_usb_bind_all">強制 Moonlight USB驅動程式接管所有支持的Xbox手柄</string>
|
||||
<string name="title_checkbox_mouse_emulation">透過手柄模擬滑鼠</string>
|
||||
<string name="summary_checkbox_mouse_emulation">長按 [Start] 鍵將會切換至滑鼠模式</string>
|
||||
<string name="title_checkbox_mouse_nav_buttons">Enable back and forward mouse buttons</string>
|
||||
<string name="summary_checkbox_mouse_nav_buttons">Enabling this option may break right clicking on some buggy devices</string>
|
||||
<string name="summary_checkbox_xb1_driver"> 為缺少原生Xbox手柄支援的設備啟用內置USB驅動 </string>
|
||||
<string name="title_checkbox_usb_bind_all"> 覆蓋安卓手柄支援 </string>
|
||||
<string name="summary_checkbox_usb_bind_all"> 強制Moonlight的USB驅動接管所有受支持的Xbox手柄 </string>
|
||||
<string name="title_checkbox_mouse_emulation"> 通過手柄類比滑鼠 </string>
|
||||
<string name="summary_checkbox_mouse_emulation"> 長按開始鍵將手柄切換為滑鼠模式 </string>
|
||||
<string name="title_checkbox_mouse_nav_buttons"> 啟用前進後退滑鼠鍵 </string>
|
||||
<string name="summary_checkbox_mouse_nav_buttons"> 在一些支援不佳的設備上啟用此項可能會使其右鍵失效 </string>
|
||||
|
||||
<string name="category_on_screen_controls_settings"> 觸控設置 </string>
|
||||
<string name="title_checkbox_show_onscreen_controls"> 啟用虛擬手柄 </string>
|
||||
<string name="summary_checkbox_show_onscreen_controls"> 將在串流畫面上顯示一層虛擬手柄 </string>
|
||||
<string name="title_checkbox_vibrate_osc">啟用震動</string>
|
||||
<string name="summary_checkbox_vibrate_osc">在手機上模擬搖桿震動</string>
|
||||
<string name="title_only_l3r3">只顯示[L3]及[R3]</string>
|
||||
<string name="summary_only_l3r3">隱藏所有虛擬按鈕除了L3和R3</string>
|
||||
<string name="title_reset_osc">重設已經保存的觸控按鈕布局</string>
|
||||
<string name="summary_reset_osc">將所有觸控按鈕重設為默認之大小和位置</string>
|
||||
<string name="dialog_title_reset_osc">重設按鈕布局</string>
|
||||
<string name="dialog_text_reset_osc">你確定要刪除所保存的按鈕布局嗎?</string>
|
||||
<string name="toast_reset_osc_success">按鈕布局已經重設</string>
|
||||
<string name="category_on_screen_controls_settings"> 螢幕控制按鈕設置 </string>
|
||||
<string name="title_checkbox_show_onscreen_controls"> 顯示幕幕控制按鈕 </string>
|
||||
<string name="summary_checkbox_show_onscreen_controls"> 在觸控式螢幕上顯示一層虛擬手柄 </string>
|
||||
<string name="title_checkbox_vibrate_osc"> 啟用震動 </string>
|
||||
<string name="summary_checkbox_vibrate_osc"> 使用螢幕控制按鈕時震動設備以類比遊戲震動效果 </string>
|
||||
<string name="title_only_l3r3"> 只顯示L3和R3 </string>
|
||||
<string name="summary_only_l3r3"> 隱藏除L3和R3外的所有虛擬按鈕 </string>
|
||||
<string name="title_reset_osc"> 重置已保存的螢幕控制按鈕佈局 </string>
|
||||
<string name="summary_reset_osc"> 重置所有螢幕控制按鈕為預設大小和位置 </string>
|
||||
<string name="dialog_title_reset_osc"> 重新開機按鈕佈局 </string>
|
||||
<string name="dialog_text_reset_osc"> 你確定要刪除所保存的螢幕按鈕佈局嗎? </string>
|
||||
<string name="toast_reset_osc_success"> 螢幕按鈕佈局已經重置 </string>
|
||||
|
||||
<string name="category_ui_settings"> 界面設置 </string>
|
||||
<string name="title_language_list">語言</string>
|
||||
<string name="summary_language_list"> 請選擇您希望Moonlight使用的語言 </string>
|
||||
<string name="title_checkbox_list_mode"> 使用列表代替圖標 </string>
|
||||
<string name="summary_checkbox_list_mode"> 將以列表來顯示電腦和遊戲 </string>
|
||||
<string name="title_checkbox_small_icon_mode"> 使用小圖標 </string>
|
||||
<string name="summary_checkbox_small_icon_mode"> 使用小圖標以在屏幕上顯示更多項目 </string>
|
||||
<string name="category_ui_settings"> 介面設置 </string>
|
||||
<string name="title_language_list"> 語言 </string>
|
||||
<string name="summary_language_list"> 選擇Moonlight顯示的語言 </string>
|
||||
<string name="title_checkbox_list_mode"> 使用清單代替圖示 </string>
|
||||
<string name="summary_checkbox_list_mode"> 清單顯示電腦和遊戲 </string>
|
||||
<string name="title_checkbox_small_icon_mode"> 使用小圖示 </string>
|
||||
<string name="summary_checkbox_small_icon_mode"> 使用小圖示以在螢幕上顯示更多專案 </string>
|
||||
|
||||
<string name="category_host_settings"> 主機設置 </string>
|
||||
<string name="title_checkbox_enable_sops"> 優化遊戲設置 </string>
|
||||
<string name="summary_checkbox_enable_sops"> 允許GeForce Experience為最佳串流效果自動更改遊戲設置 </string>
|
||||
<string name="title_checkbox_host_audio"> 將聲音輸出到電腦上 </string>
|
||||
<string name="summary_checkbox_host_audio"> 將在電腦和本設備同時輸出聲音 </string>
|
||||
<string name="summary_checkbox_enable_sops"> 允許GFE為最佳串流效果自動更改遊戲設置 </string>
|
||||
<string name="title_checkbox_host_audio"> 在電腦上播放聲音 </string>
|
||||
<string name="summary_checkbox_host_audio"> 在電腦和本設備同時輸出聲音 </string>
|
||||
|
||||
<string name="category_advanced_settings"> 高級設置 </string>
|
||||
<string name="title_disable_frame_drop">永遠不掉幀</string>
|
||||
<string name="summary_disable_frame_drop">在一些手機上可能可以減少micro-stuttering, 但可能導致延遲</string>
|
||||
<string name="title_video_format"> H.265設置 </string>
|
||||
<string name="summary_video_format">H.265能降低頻寬需求,但是需要設備支持 </string>
|
||||
<string name="title_enable_hdr">啟用 HDR (實驗中)</string>
|
||||
<string name="summary_enable_hdr">啟用 HDR 當遊戲與電腦的顯示卡支援時. HDR 需要 GTX 1000 系列之顯示卡或更高.</string>
|
||||
<string name="title_disable_frame_drop"> 永不掉幀 </string>
|
||||
<string name="summary_disable_frame_drop"> 可能會減少在一些設備上的卡頓,但會增加延遲 </string>
|
||||
<string name="title_video_format"> 更改H.265設置 </string>
|
||||
<string name="summary_video_format">H.265能降低視頻頻寬需求,但需要較新的設備才能支援</string>
|
||||
<string name="title_enable_hdr"> 啟用 HDR (實驗) </string>
|
||||
<string name="summary_enable_hdr"> 當遊戲和顯卡支援時以HDR模式串流。 HDR需要GTX 1000系列或更高規格顯卡。 </string>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<string name="title_enable_perf_overlay"> 啟用性能資訊 </string>
|
||||
<string name="summary_enable_perf_overlay"> 在串流中顯示即時性能資訊 </string>
|
||||
<string name="title_osc_opacity">更改屏幕按鈕透明度</string>
|
||||
<string name="dialog_title_osc_opacity">透明度</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
<string name="summary_osc_opacity">令屏幕按钮變得更透明/更不透明</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -30,31 +30,44 @@
|
||||
<item>120</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="audio_config_names">
|
||||
<item>Stereo</item>
|
||||
<item>5.1 Surround Sound</item>
|
||||
<item>7.1 Surround Sound</item>
|
||||
</string-array>
|
||||
<string-array name="audio_config_values" translatable="false">
|
||||
<item>2</item>
|
||||
<item>51</item>
|
||||
<item>71</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="language_names" translatable="false">
|
||||
<item>Default</item>
|
||||
<item>English</item>
|
||||
<item>Italiano</item>
|
||||
<item>日本語</item>
|
||||
<item>Русский</item>
|
||||
<item>Nederlands</item>
|
||||
<item>中文(简体)</item>
|
||||
<item>中文(繁體)</item>
|
||||
<item>Korean</item>
|
||||
<item>Español</item>
|
||||
<item>French</item>
|
||||
<item>Default</item>
|
||||
<item>English</item>
|
||||
<item>Italiano</item>
|
||||
<item>日本語</item>
|
||||
<item>Русский</item>
|
||||
<item>Nederlands</item>
|
||||
<item>中文(简体)</item>
|
||||
<item>中文(繁體)</item>
|
||||
<item>Korean</item>
|
||||
<item>Español</item>
|
||||
<item>Français</item>
|
||||
<item>Deutsch</item>
|
||||
</string-array>
|
||||
<string-array name="language_values" translatable="false">
|
||||
<item>default</item>
|
||||
<item>en</item>
|
||||
<item>it</item>
|
||||
<item>ja</item>
|
||||
<item>ru</item>
|
||||
<item>nl</item>
|
||||
<item>zh-CN</item>
|
||||
<item>zh-TW</item>
|
||||
<item>ko</item>
|
||||
<item>es</item>
|
||||
<item>fr</item>
|
||||
<item>default</item>
|
||||
<item>en</item>
|
||||
<item>it</item>
|
||||
<item>ja</item>
|
||||
<item>ru</item>
|
||||
<item>nl</item>
|
||||
<item>zh-CN</item>
|
||||
<item>zh-TW</item>
|
||||
<item>ko</item>
|
||||
<item>es</item>
|
||||
<item>fr</item>
|
||||
<item>de</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="decoder_names">
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
<string name="message_decoding_reset">Your device\'s video decoder continues to crash at your selected streaming settings. Your streaming settings have been reset to default.</string>
|
||||
<string name="error_usb_prohibited">USB access is prohibited by your device administrator. Check your Knox or MDM settings.</string>
|
||||
<string name="unable_to_pin_shortcut">Your current launcher does not allow for creating pinned shortcuts.</string>
|
||||
<string name="video_decoder_init_failed">Video decoder failed to initialize. Your device may not support the selected resolution or frame rate.</string>
|
||||
|
||||
<!-- Start application messages -->
|
||||
<string name="conn_establishing_title">Establishing Connection</string>
|
||||
@@ -134,8 +135,8 @@
|
||||
<string name="summary_checkbox_enable_pip">Allows the stream to be viewed (but not controlled) while multitasking</string>
|
||||
|
||||
<string name="category_audio_settings">Audio Settings</string>
|
||||
<string name="title_checkbox_51_surround">Enable 5.1 surround sound</string>
|
||||
<string name="summary_checkbox_51_surround">Uncheck if you experience audio issues. Requires GFE 2.7 or higher.</string>
|
||||
<string name="title_audio_config_list">Surround sound configuration</string>
|
||||
<string name="summary_audio_config_list">Enable 5.1 or 7.1 surround sound for home-theater systems</string>
|
||||
|
||||
<string name="category_input_settings">Input Settings</string>
|
||||
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
|
||||
@@ -165,6 +166,10 @@
|
||||
<string name="dialog_title_reset_osc">Reset Layout</string>
|
||||
<string name="dialog_text_reset_osc">Are you sure you want to delete your saved on-screen controls layout?</string>
|
||||
<string name="toast_reset_osc_success">On-screen controls reset to default</string>
|
||||
<string name="title_osc_opacity">Change opacity of on-screen controls</string>
|
||||
<string name="summary_osc_opacity">Make the on-screen controls more/less transparent</string>
|
||||
<string name="dialog_title_osc_opacity">Change opacity</string>
|
||||
<string name="suffix_osc_opacity">%</string>
|
||||
|
||||
<string name="category_ui_settings">UI Settings</string>
|
||||
<string name="title_language_list">Language</string>
|
||||
|
||||
@@ -43,11 +43,13 @@
|
||||
android:defaultValue="false" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/category_audio_settings">
|
||||
<CheckBoxPreference
|
||||
android:key="checkbox_51_surround"
|
||||
android:title="@string/title_checkbox_51_surround"
|
||||
android:summary="@string/summary_checkbox_51_surround"
|
||||
android:defaultValue="false" />
|
||||
<ListPreference
|
||||
android:key="list_audio_config"
|
||||
android:title="@string/title_audio_config_list"
|
||||
android:summary="@string/summary_audio_config_list"
|
||||
android:entries="@array/audio_config_names"
|
||||
android:entryValues="@array/audio_config_values"
|
||||
android:defaultValue="2" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/category_input_settings">
|
||||
<!--com.limelight.preferences.SeekBarPreference
|
||||
@@ -107,6 +109,17 @@
|
||||
android:key="checkbox_only_show_L3R3"
|
||||
android:summary="@string/summary_only_l3r3"
|
||||
android:title="@string/title_only_l3r3" />
|
||||
<com.limelight.preferences.SeekBarPreference
|
||||
android:key="seekbar_osc_opacity"
|
||||
android:dependency="checkbox_show_onscreen_controls"
|
||||
android:dialogMessage="@string/summary_osc_opacity"
|
||||
seekbar:min="0"
|
||||
seekbar:step="1"
|
||||
android:max="100"
|
||||
android:defaultValue="90"
|
||||
android:summary="@string/summary_osc_opacity"
|
||||
android:text="@string/suffix_osc_opacity"
|
||||
android:title="@string/dialog_title_osc_opacity" />
|
||||
<com.limelight.preferences.ConfirmDeleteOscPreference
|
||||
android:title="@string/title_reset_osc"
|
||||
android:summary="@string/summary_reset_osc"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
- Fixed H.264 colorspace data on Android 8.0 and later
|
||||
@@ -0,0 +1,4 @@
|
||||
- Improved layout and appearance of the on-screen controls
|
||||
- Added support for resizing the on-screen controls
|
||||
- Added German translation
|
||||
- Updated Chinese and French translations
|
||||
@@ -0,0 +1,6 @@
|
||||
- Latency optimizations for Android R DP1
|
||||
- Video decoder optimizations
|
||||
- Added Start+Back+LB+RB combo to disconnect the stream
|
||||
- Fixed back button on some Android TV remotes
|
||||
- Fixed d-pad on gamepads that expose non-functional hat axes
|
||||
- Fixed unexpected mouse input after using gamepad mouse emulation mode
|
||||
@@ -0,0 +1,4 @@
|
||||
- Configurable transparency for on-screen controls
|
||||
- Fixed a crash when pinning an app shortcut to the home screen
|
||||
- Fixed right click unexpectedly stopping the stream on some devices
|
||||
- Improved key repeating behavior while streaming
|
||||
@@ -0,0 +1,2 @@
|
||||
- Improved performance during periods of packet loss
|
||||
- Improved prioritization of control data sent back to the PC
|
||||
@@ -0,0 +1,2 @@
|
||||
- Added 7.1 surround sound support
|
||||
- Fixed a crash on some devices during a high intensity rumble event
|
||||
@@ -6,7 +6,7 @@ Streaming performance may vary based on your client device and network setup. HD
|
||||
* Open-source and completely free (no ads, IAPs, or "Pro")
|
||||
* Streams games purchased from any store
|
||||
* Works on your home network or over the Internet/LTE
|
||||
* Up to 4K 120 FPS HDR streaming with 5.1 surround sound
|
||||
* Up to 4K 120 FPS HDR streaming with 7.1 surround sound
|
||||
* Keyboard and mouse support (with Android 8.0 or rooted device)
|
||||
* Supports PS3, PS4, Xbox 360, Xbox One, and Android gamepads
|
||||
* Force feedback support
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#Tue Aug 20 11:37:45 PDT 2019
|
||||
#Mon Feb 24 12:32:24 PST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
include ':app', ':moonlight-common'
|
||||
include ':app'
|
||||
|
||||
Reference in New Issue
Block a user