Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 32b6ec166a | |||
| 3b2eb38d9a | |||
| cfa28298bc | |||
| 6c077f5289 | |||
| 3319749c0d | |||
| 702e1826d3 | |||
| f3a1cd3aef | |||
| d9dedf6f19 | |||
| f10085f552 | |||
| eb7f0887bf | |||
| 34a9132d60 | |||
| 24d3fb000a | |||
| b7b6adaff7 | |||
| 85ed72802f | |||
| f54f8c83e7 | |||
| 124bfdf418 | |||
| 01507d9995 | |||
| 070c82bc44 | |||
| 17df15293f | |||
| 6551076613 |
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"MD013": false
|
||||
}
|
||||
@@ -13,17 +13,50 @@ Moonlight also has a [PC client](https://github.com/moonlight-stream/moonlight-q
|
||||
You can follow development on our [Discord server](https://moonlight-stream.org/discord) and help translate Moonlight into your language on [Weblate](https://hosted.weblate.org/projects/moonlight/moonlight-android/).
|
||||
|
||||
## Downloads
|
||||
|
||||
* [Google Play Store](https://play.google.com/store/apps/details?id=com.limelight)
|
||||
* [Amazon App Store](https://www.amazon.com/gp/product/B00JK4MFN2)
|
||||
* [F-Droid](https://f-droid.org/packages/com.limelight)
|
||||
* [APK](https://github.com/moonlight-stream/moonlight-android/releases)
|
||||
|
||||
## Building
|
||||
|
||||
* Install Android Studio and the Android NDK
|
||||
* Run ‘git submodule update --init --recursive’ from within moonlight-android/
|
||||
* In moonlight-android/, create a file called ‘local.properties’. Add an ‘ndk.dir=’ property to the local.properties file and set it equal to your NDK directory.
|
||||
* Build the APK using Android Studio or gradle
|
||||
|
||||
## Building with Nix
|
||||
|
||||
A `flake.nix` provides a reproducible dev environment and a fully hermetic
|
||||
debug-APK build (Android SDK 34, NDK 27.0.12077973, Gradle, JDK 17). All
|
||||
commands need `?submodules=1` so the `moonlight-common-c` + `enet` submodule
|
||||
sources are included in the build.
|
||||
|
||||
```sh
|
||||
# Dev shell: ANDROID_HOME / ANDROID_NDK_ROOT / JAVA_HOME + gradle, then build by hand
|
||||
nix develop '.?submodules=1'
|
||||
./gradlew assembleNonRootDebug
|
||||
|
||||
# Hermetic, offline build of the nonRoot debug APK (output in ./result/)
|
||||
nix build '.?submodules=1#moonlight-android'
|
||||
ls result/app-nonRoot-debug.apk
|
||||
```
|
||||
|
||||
Maven dependencies are pinned in `deps.json` (a Nixpkgs Gradle mitm-cache
|
||||
lockfile). Regenerate it after changing dependencies:
|
||||
|
||||
```sh
|
||||
nix build '.?submodules=1#moonlight-android.mitmCache.updateScript' && ./result
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
* If your Nix is configured to offload to a remote builder, add `--builders ""`
|
||||
to build locally instead of copying the multi-GB SDK closure to the remote.
|
||||
* The build targets the `nonRoot` `debug` variant; it is signed with the
|
||||
generated Android debug keystore (not a release key).
|
||||
|
||||
## Authors
|
||||
|
||||
* [Cameron Gutman](https://github.com/cgutman)
|
||||
|
||||
+7
-9
@@ -1,14 +1,14 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
ndkVersion "23.2.8568313"
|
||||
ndkVersion "27.0.12077973"
|
||||
|
||||
compileSdk 34
|
||||
|
||||
namespace 'com.limelight'
|
||||
|
||||
defaultConfig {
|
||||
minSdk 16
|
||||
minSdk 21
|
||||
targetSdk 34
|
||||
|
||||
versionName "12.1"
|
||||
@@ -136,12 +136,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
|
||||
implementation 'org.jcodec:jcodec:0.2.3'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.12.13'
|
||||
implementation 'com.squareup.okio:okio:1.17.5'
|
||||
// 3.5.8 requires minSdk 19, uses StandardCharsets.UTF_8 internally
|
||||
implementation 'org.jmdns:jmdns:3.5.7'
|
||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
|
||||
implementation 'org.jcodec:jcodec:0.2.5'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
implementation 'org.jmdns:jmdns:3.5.9'
|
||||
implementation 'com.github.cgutman:ShieldControllerExtensions:1.0.1'
|
||||
}
|
||||
|
||||
@@ -195,14 +195,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
// If we're going to use immersive mode, we want to have
|
||||
// the entire screen
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
||||
}
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
||||
|
||||
// Listen for UI visibility events
|
||||
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
|
||||
@@ -560,39 +558,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
|
||||
if (desiredOrientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
|
||||
}
|
||||
else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
}
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
|
||||
}
|
||||
else if (desiredOrientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
|
||||
}
|
||||
else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
|
||||
}
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
|
||||
}
|
||||
else {
|
||||
// If we don't have a reason to lock to portrait or landscape, allow any orientation
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
|
||||
}
|
||||
else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
|
||||
}
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// For regular displays, we always request landscape
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
|
||||
}
|
||||
else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
}
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -920,7 +898,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
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) {
|
||||
else {
|
||||
float bestRefreshRate = display.getRefreshRate();
|
||||
for (float candidate : display.getSupportedRefreshRates()) {
|
||||
LimeLog.info("Examining refresh rate: "+candidate);
|
||||
@@ -944,19 +922,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Apply the refresh rate change
|
||||
getWindow().setAttributes(windowLayoutParams);
|
||||
}
|
||||
else {
|
||||
// Otherwise, the active display refresh rate is just
|
||||
// whatever is currently in use.
|
||||
displayRefreshRate = display.getRefreshRate();
|
||||
}
|
||||
|
||||
// From 4.4 to 5.1 we can't ask for a 4K display mode, so we'll
|
||||
// Until Marshmallow, we can't ask for a 4K display mode, so we'll
|
||||
// need to hint the OS to provide one.
|
||||
boolean aspectRatioMatch = false;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
|
||||
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
// On KitKat and later (where we can use the whole screen via immersive mode), we'll
|
||||
// calculate whether we need to scale by aspect ratio or not. If not, we'll use
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
// We'll calculate whether we need to scale by aspect ratio. If not, we'll use
|
||||
// setFixedSize so we can handle 4K properly. The only known devices that have
|
||||
// >= 4K screens have exactly 4K screens, so we'll be able to hit this good path
|
||||
// on these devices. On Marshmallow, we can start changing to 4K manually but no
|
||||
@@ -1011,8 +982,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
Game.this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
// Use immersive mode on 4.4+ or standard low profile on previous builds
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
else {
|
||||
// Use immersive mode
|
||||
Game.this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
@@ -1021,11 +992,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
else {
|
||||
Game.this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_LOW_PROFILE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2214,13 +2180,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
// Tell the OS not to buffer input events for us
|
||||
//
|
||||
// NB: This is still needed even when we call the newer requestUnbufferedDispatch()!
|
||||
view.requestUnbufferedDispatch(event);
|
||||
}
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
// Tell the OS not to buffer input events for us
|
||||
//
|
||||
// NB: This is still needed even when we call the newer requestUnbufferedDispatch()!
|
||||
view.requestUnbufferedDispatch(event);
|
||||
}
|
||||
|
||||
return handleMotionEvent(view, event);
|
||||
@@ -2667,14 +2631,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||
hideSystemUi(2000);
|
||||
}
|
||||
// This flag is only set on 4.4+
|
||||
else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT &&
|
||||
(visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
|
||||
hideSystemUi(2000);
|
||||
}
|
||||
// This flag is only set before 4.4+
|
||||
else if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT &&
|
||||
(visibility & View.SYSTEM_UI_FLAG_LOW_PROFILE) == 0) {
|
||||
else if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
|
||||
hideSystemUi(2000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,26 @@ import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.limelight.computers.ComputerDatabaseManager;
|
||||
import com.limelight.computers.ComputerManagerListener;
|
||||
import com.limelight.computers.ComputerManagerService;
|
||||
import com.limelight.nvstream.http.ComputerDetails;
|
||||
import com.limelight.nvstream.http.NvApp;
|
||||
import com.limelight.nvstream.http.NvHTTP;
|
||||
import com.limelight.nvstream.http.PairingManager;
|
||||
import com.limelight.nvstream.wol.WakeOnLanSender;
|
||||
import com.limelight.utils.CacheHelper;
|
||||
import com.limelight.utils.Dialog;
|
||||
import com.limelight.utils.ServerHelper;
|
||||
import com.limelight.utils.SpinnerDialog;
|
||||
import com.limelight.utils.UiHelper;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ShortcutTrampoline extends Activity {
|
||||
@@ -33,6 +40,7 @@ public class ShortcutTrampoline extends Activity {
|
||||
private SpinnerDialog blockingLoadSpinner;
|
||||
|
||||
private ComputerManagerService.ComputerManagerBinder managerBinder;
|
||||
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder binder) {
|
||||
final ComputerManagerService.ComputerManagerBinder localBinder =
|
||||
@@ -214,9 +222,9 @@ public class ShortcutTrampoline extends Activity {
|
||||
}
|
||||
};
|
||||
|
||||
protected boolean validateInput(String uuidString, String appIdString) {
|
||||
// Validate UUID
|
||||
if (uuidString == null) {
|
||||
protected boolean validateInput(String uuidString, String appIdString, String nameString) {
|
||||
// Validate PC UUID/Name
|
||||
if (uuidString == null && nameString == null) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_invalid_uuid),
|
||||
@@ -224,14 +232,25 @@ public class ShortcutTrampoline extends Activity {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
UUID.fromString(uuidString);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_invalid_uuid),
|
||||
true);
|
||||
return false;
|
||||
if (uuidString != null && !uuidString.isEmpty()) {
|
||||
try {
|
||||
UUID.fromString(uuidString);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_invalid_uuid),
|
||||
true);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// UUID is null, so fallback to Name
|
||||
if (nameString == null || nameString.isEmpty()) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_invalid_uuid),
|
||||
true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate App ID (if provided)
|
||||
@@ -255,24 +274,93 @@ public class ShortcutTrampoline extends Activity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
UiHelper.notifyNewRootView(this);
|
||||
ComputerDatabaseManager dbManager = new ComputerDatabaseManager(this);
|
||||
ComputerDetails _computer = null;
|
||||
|
||||
String appIdString = getIntent().getStringExtra(Game.EXTRA_APP_ID);
|
||||
// PC arguments, both are optional, but at least one must be provided
|
||||
uuidString = getIntent().getStringExtra(AppView.UUID_EXTRA);
|
||||
String nameString = getIntent().getStringExtra(AppView.NAME_EXTRA);
|
||||
|
||||
if (validateInput(uuidString, appIdString)) {
|
||||
if (appIdString != null && !appIdString.isEmpty()) {
|
||||
app = new NvApp(getIntent().getStringExtra(Game.EXTRA_APP_NAME),
|
||||
Integer.parseInt(appIdString),
|
||||
getIntent().getBooleanExtra(Game.EXTRA_APP_HDR, false));
|
||||
// App arguments, both are optional, but one must be provided in order to start an app
|
||||
String appIdString = getIntent().getStringExtra(Game.EXTRA_APP_ID);
|
||||
String appNameString = getIntent().getStringExtra(Game.EXTRA_APP_NAME);
|
||||
|
||||
if (!validateInput(uuidString, appIdString, nameString)) {
|
||||
// Invalid input, so just return
|
||||
return;
|
||||
}
|
||||
|
||||
if (uuidString == null || uuidString.isEmpty()) {
|
||||
// Use nameString to find the corresponding UUID
|
||||
_computer = dbManager.getComputerByName(nameString);
|
||||
|
||||
if (_computer == null) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_pc_not_found),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind to the computer manager service
|
||||
bindService(new Intent(this, ComputerManagerService.class), serviceConnection,
|
||||
Service.BIND_AUTO_CREATE);
|
||||
uuidString = _computer.uuid;
|
||||
|
||||
blockingLoadSpinner = SpinnerDialog.displayDialog(this, getResources().getString(R.string.conn_establishing_title),
|
||||
getResources().getString(R.string.applist_connect_msg), true);
|
||||
// Set the AppView UUID intent, since it wasn't provided
|
||||
setIntent(new Intent(getIntent()).putExtra(AppView.UUID_EXTRA, uuidString));
|
||||
}
|
||||
|
||||
if (appIdString != null && !appIdString.isEmpty()) {
|
||||
app = new NvApp(getIntent().getStringExtra(Game.EXTRA_APP_NAME),
|
||||
Integer.parseInt(appIdString),
|
||||
getIntent().getBooleanExtra(Game.EXTRA_APP_HDR, false));
|
||||
}
|
||||
else if (appNameString != null && !appNameString.isEmpty()) {
|
||||
// Use appNameString to find the corresponding AppId
|
||||
try {
|
||||
int appId = -1;
|
||||
String rawAppList = CacheHelper.readInputStreamToString(CacheHelper.openCacheFileForInput(getCacheDir(), "applist", uuidString));
|
||||
|
||||
if (rawAppList.isEmpty()) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_invalid_app_id),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
List<NvApp> applist = NvHTTP.getAppListByReader(new StringReader(rawAppList));
|
||||
|
||||
for (NvApp _app : applist) {
|
||||
if (_app.getAppName().equals(appNameString)) {
|
||||
appId = _app.getAppId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (appId < 0) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_invalid_app_id),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
setIntent(new Intent(getIntent()).putExtra(Game.EXTRA_APP_ID, appId));
|
||||
app = new NvApp(
|
||||
appNameString,
|
||||
appId,
|
||||
getIntent().getBooleanExtra(Game.EXTRA_APP_HDR, false));
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
Dialog.displayDialog(ShortcutTrampoline.this,
|
||||
getResources().getString(R.string.conn_error_title),
|
||||
getResources().getString(R.string.scut_invalid_app_id),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Bind to the computer manager service
|
||||
bindService(new Intent(this, ComputerManagerService.class), serviceConnection,
|
||||
Service.BIND_AUTO_CREATE);
|
||||
|
||||
blockingLoadSpinner = SpinnerDialog.displayDialog(this, getResources().getString(R.string.conn_establishing_title),
|
||||
getResources().getString(R.string.applist_connect_msg), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,51 +26,41 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
||||
}
|
||||
|
||||
private AudioTrack createAudioTrack(int channelConfig, int sampleRate, int bufferSize, boolean lowLatency) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||
sampleRate,
|
||||
channelConfig,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
bufferSize,
|
||||
AudioTrack.MODE_STREAM);
|
||||
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_GAME);
|
||||
AudioFormat format = new AudioFormat.Builder()
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.setSampleRate(sampleRate)
|
||||
.setChannelMask(channelConfig)
|
||||
.build();
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
// Use FLAG_LOW_LATENCY on L through N
|
||||
if (lowLatency) {
|
||||
attributesBuilder.setFlags(AudioAttributes.FLAG_LOW_LATENCY);
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AudioTrack.Builder trackBuilder = new AudioTrack.Builder()
|
||||
.setAudioFormat(format)
|
||||
.setAudioAttributes(attributesBuilder.build())
|
||||
.setTransferMode(AudioTrack.MODE_STREAM)
|
||||
.setBufferSizeInBytes(bufferSize);
|
||||
|
||||
// Use PERFORMANCE_MODE_LOW_LATENCY on O and later
|
||||
if (lowLatency) {
|
||||
trackBuilder.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY);
|
||||
}
|
||||
|
||||
return trackBuilder.build();
|
||||
}
|
||||
else {
|
||||
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_GAME);
|
||||
AudioFormat format = new AudioFormat.Builder()
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.setSampleRate(sampleRate)
|
||||
.setChannelMask(channelConfig)
|
||||
.build();
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
// Use FLAG_LOW_LATENCY on L through N
|
||||
if (lowLatency) {
|
||||
attributesBuilder.setFlags(AudioAttributes.FLAG_LOW_LATENCY);
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AudioTrack.Builder trackBuilder = new AudioTrack.Builder()
|
||||
.setAudioFormat(format)
|
||||
.setAudioAttributes(attributesBuilder.build())
|
||||
.setTransferMode(AudioTrack.MODE_STREAM)
|
||||
.setBufferSizeInBytes(bufferSize);
|
||||
|
||||
// Use PERFORMANCE_MODE_LOW_LATENCY on O and later
|
||||
if (lowLatency) {
|
||||
trackBuilder.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY);
|
||||
}
|
||||
|
||||
return trackBuilder.build();
|
||||
}
|
||||
else {
|
||||
return new AudioTrack(attributesBuilder.build(),
|
||||
format,
|
||||
bufferSize,
|
||||
AudioTrack.MODE_STREAM,
|
||||
AudioManager.AUDIO_SESSION_ID_GENERATE);
|
||||
}
|
||||
return new AudioTrack(attributesBuilder.build(),
|
||||
format,
|
||||
bufferSize,
|
||||
AudioTrack.MODE_STREAM,
|
||||
AudioManager.AUDIO_SESSION_ID_GENERATE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,20 +81,10 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||
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;
|
||||
}
|
||||
// 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
|
||||
break;
|
||||
default:
|
||||
LimeLog.severe("Decoder returned unhandled channel count");
|
||||
|
||||
@@ -666,7 +666,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
// back button to function for navigation.
|
||||
//
|
||||
// First, check if this is an internal device we're being called on.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !isExternal(dev)) {
|
||||
if (!isExternal(dev)) {
|
||||
InputManager im = (InputManager) activityContext.getSystemService(Context.INPUT_SERVICE);
|
||||
|
||||
boolean foundInternalGamepad = false;
|
||||
@@ -712,10 +712,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
String devName = dev.getName();
|
||||
|
||||
LimeLog.info("Creating controller context for device: "+devName);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
LimeLog.info("Vendor ID: "+dev.getVendorId());
|
||||
LimeLog.info("Product ID: "+dev.getProductId());
|
||||
}
|
||||
LimeLog.info("Vendor ID: " + dev.getVendorId());
|
||||
LimeLog.info("Product ID: "+dev.getProductId());
|
||||
LimeLog.info(dev.toString());
|
||||
|
||||
context.inputDevice = dev;
|
||||
@@ -723,15 +721,13 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
context.id = dev.getId();
|
||||
context.external = isExternal(dev);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
context.vendorId = dev.getVendorId();
|
||||
context.productId = dev.getProductId();
|
||||
context.vendorId = dev.getVendorId();
|
||||
context.productId = dev.getProductId();
|
||||
|
||||
// These aren't always present in the Android key layout files, so they won't show up
|
||||
// in our normal InputDevice.hasKeys() probing.
|
||||
context.hasPaddles = MoonBridge.guessControllerHasPaddles(context.vendorId, context.productId);
|
||||
context.hasShare = MoonBridge.guessControllerHasShareButton(context.vendorId, context.productId);
|
||||
}
|
||||
// These aren't always present in the Android key layout files, so they won't show up
|
||||
// in our normal InputDevice.hasKeys() probing.
|
||||
context.hasPaddles = MoonBridge.guessControllerHasPaddles(context.vendorId, context.productId);
|
||||
context.hasShare = MoonBridge.guessControllerHasShareButton(context.vendorId, context.productId);
|
||||
|
||||
// Try to use the InputDevice's associated vibrators first
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && hasQuadAmplitudeControlledRumbleVibrators(dev.getVibratorManager())) {
|
||||
@@ -790,11 +786,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
// Detect if the gamepad has Mode and Select buttons according to the Android key layouts.
|
||||
// We do this first because other codepaths below may override these defaults.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
boolean[] buttons = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_MODE, KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BACK, 0);
|
||||
context.hasMode = buttons[0];
|
||||
context.hasSelect = buttons[1] || buttons[2];
|
||||
}
|
||||
boolean[] buttons = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_MODE, KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BACK, 0);
|
||||
context.hasMode = buttons[0];
|
||||
context.hasSelect = buttons[1] || buttons[2];
|
||||
|
||||
context.touchpadXRange = dev.getMotionRange(MotionEvent.AXIS_X, InputDevice.SOURCE_TOUCHPAD);
|
||||
context.touchpadYRange = dev.getMotionRange(MotionEvent.AXIS_Y, InputDevice.SOURCE_TOUCHPAD);
|
||||
@@ -844,22 +838,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
||||
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
||||
if (rxRange != null && ryRange != null && devName != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (dev.getVendorId() == 0x054c) { // Sony
|
||||
if (dev.hasKeys(KeyEvent.KEYCODE_BUTTON_C)[0]) {
|
||||
LimeLog.info("Detected non-standard DualShock 4 mapping");
|
||||
context.isNonStandardDualShock4 = true;
|
||||
}
|
||||
else {
|
||||
LimeLog.info("Detected DualShock 4 (Linux standard mapping)");
|
||||
context.usesLinuxGamepadStandardFaceButtons = true;
|
||||
}
|
||||
if (dev.getVendorId() == 0x054c) { // Sony
|
||||
if (dev.hasKeys(KeyEvent.KEYCODE_BUTTON_C)[0]) {
|
||||
LimeLog.info("Detected non-standard DualShock 4 mapping");
|
||||
context.isNonStandardDualShock4 = true;
|
||||
} else {
|
||||
LimeLog.info("Detected DualShock 4 (Linux standard mapping)");
|
||||
context.usesLinuxGamepadStandardFaceButtons = true;
|
||||
}
|
||||
}
|
||||
else if (!devName.contains("Xbox") && !devName.contains("XBox") && !devName.contains("X-Box")) {
|
||||
LimeLog.info("Assuming non-standard DualShock 4 mapping on < 4.4");
|
||||
context.isNonStandardDualShock4 = true;
|
||||
}
|
||||
|
||||
if (context.isNonStandardDualShock4) {
|
||||
// The old DS4 driver uses RX and RY for triggers
|
||||
@@ -943,16 +930,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
// The ADT-1 controller needs a similar fixup to the ASUS Gamepad
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// The device name provided is just "Gamepad" which is pretty useless, so we
|
||||
// use VID/PID instead
|
||||
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
|
||||
context.backIsStart = true;
|
||||
context.modeIsSelect = true;
|
||||
context.triggerDeadzone = 0.30f;
|
||||
context.hasSelect = true;
|
||||
context.hasMode = false;
|
||||
}
|
||||
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
|
||||
context.backIsStart = true;
|
||||
context.modeIsSelect = true;
|
||||
context.triggerDeadzone = 0.30f;
|
||||
context.hasSelect = true;
|
||||
context.hasMode = false;
|
||||
}
|
||||
|
||||
context.ignoreBack = shouldIgnoreBack(dev);
|
||||
@@ -962,16 +945,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
// use the back button as start since it doesn't have a start/menu button
|
||||
// on the controller
|
||||
if (devName.contains("ASUS Gamepad")) {
|
||||
// We can only do this check on KitKat or higher, but it doesn't matter since ATV
|
||||
// is Android 5.0 anyway
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
|
||||
if (!hasStartKey[0] && !hasStartKey[1]) {
|
||||
context.backIsStart = true;
|
||||
context.modeIsSelect = true;
|
||||
context.hasSelect = true;
|
||||
context.hasMode = false;
|
||||
}
|
||||
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
|
||||
if (!hasStartKey[0] && !hasStartKey[1]) {
|
||||
context.backIsStart = true;
|
||||
context.modeIsSelect = true;
|
||||
context.hasSelect = true;
|
||||
context.hasMode = false;
|
||||
}
|
||||
|
||||
// The ASUS Gamepad has triggers that sit far forward and are prone to false presses
|
||||
@@ -1016,10 +995,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
// Thrustmaster Score A gamepad home button reports directly to android as
|
||||
// KEY_HOMEPAGE event on another event channel
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (dev.getVendorId() == 0x044f && dev.getProductId() == 0xb328) {
|
||||
context.hasMode = false;
|
||||
}
|
||||
if (dev.getVendorId() == 0x044f && dev.getProductId() == 0xb328) {
|
||||
context.hasMode = false;
|
||||
}
|
||||
|
||||
LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius);
|
||||
@@ -2048,15 +2025,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
.build();
|
||||
vibrator.vibrate(VibrationEffect.createWaveform(new long[]{0, onTime, offTime}, 0), vibrationAttributes);
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
else {
|
||||
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_GAME)
|
||||
.build();
|
||||
vibrator.vibrate(new long[]{0, onTime, offTime}, 0, audioAttributes);
|
||||
}
|
||||
else {
|
||||
vibrator.vibrate(new long[]{0, onTime, offTime}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleRumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
|
||||
@@ -3086,11 +3060,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
|
||||
@Override
|
||||
public void sendControllerArrival() {
|
||||
// Below KitKat we can't get enough information to report controller details accurately
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte type;
|
||||
switch (inputDevice.getVendorId()) {
|
||||
case 0x045e: // Microsoft
|
||||
|
||||
@@ -211,28 +211,22 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
||||
}
|
||||
|
||||
public static boolean isRecognizedInputDevice(UsbDevice device) {
|
||||
// On KitKat and later, we can determine if this VID and PID combo
|
||||
// matches an existing input device and defer to the built-in controller
|
||||
// support in that case. Prior to KitKat, we'll always return true to be safe.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
for (int id : InputDevice.getDeviceIds()) {
|
||||
InputDevice inputDev = InputDevice.getDevice(id);
|
||||
if (inputDev == null) {
|
||||
// Device was removed while looping
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inputDev.getVendorId() == device.getVendorId() &&
|
||||
inputDev.getProductId() == device.getProductId()) {
|
||||
return true;
|
||||
}
|
||||
// Determine if this VID and PID combo matches an existing input device
|
||||
// and defer to the built-in controller support in that case.
|
||||
for (int id : InputDevice.getDeviceIds()) {
|
||||
InputDevice inputDev = InputDevice.getDevice(id);
|
||||
if (inputDev == null) {
|
||||
// Device was removed while looping
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
if (inputDev.getVendorId() == device.getVendorId() &&
|
||||
inputDev.getProductId() == device.getProductId()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean kernelSupportsXboxOne() {
|
||||
|
||||
@@ -39,6 +39,7 @@ public class Xbox360Controller extends AbstractXboxController {
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x2f24, // GameSir
|
||||
0x2dc8, // 8BitDo
|
||||
};
|
||||
|
||||
public static boolean canClaimDevice(UsbDevice device) {
|
||||
|
||||
@@ -89,28 +89,26 @@ public class Xbox360WirelessDongle extends AbstractController {
|
||||
public boolean start() {
|
||||
int controllerIndex = 0;
|
||||
|
||||
// On KitKat, there is a controller number associated with input devices.
|
||||
// On Android, there is a controller number associated with input devices.
|
||||
// We can use this to approximate the likely controller number. This won't
|
||||
// be completely accurate because there's no guarantee the order of interfaces
|
||||
// matches the order that devices were enumerated by xpad, but it's probably
|
||||
// better than nothing.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
for (int id : InputDevice.getDeviceIds()) {
|
||||
InputDevice inputDev = InputDevice.getDevice(id);
|
||||
if (inputDev == null) {
|
||||
// Device was removed while looping
|
||||
continue;
|
||||
}
|
||||
for (int id : InputDevice.getDeviceIds()) {
|
||||
InputDevice inputDev = InputDevice.getDevice(id);
|
||||
if (inputDev == null) {
|
||||
// Device was removed while looping
|
||||
continue;
|
||||
}
|
||||
|
||||
// Newer xpad versions use a special product ID (0x02a1) for controllers
|
||||
// rather than copying the product ID of the dongle itself.
|
||||
if (inputDev.getVendorId() == device.getVendorId() &&
|
||||
(inputDev.getProductId() == device.getProductId() ||
|
||||
inputDev.getProductId() == 0x02a1) &&
|
||||
inputDev.getControllerNumber() > 0) {
|
||||
controllerIndex = inputDev.getControllerNumber() - 1;
|
||||
break;
|
||||
}
|
||||
// Newer xpad versions use a special product ID (0x02a1) for controllers
|
||||
// rather than copying the product ID of the dongle itself.
|
||||
if (inputDev.getVendorId() == device.getVendorId() &&
|
||||
(inputDev.getProductId() == device.getProductId() ||
|
||||
inputDev.getProductId() == 0x02a1) &&
|
||||
inputDev.getControllerNumber() > 0) {
|
||||
controllerIndex = inputDev.getControllerNumber() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -185,6 +185,10 @@ public class VirtualControllerConfigurationLoader {
|
||||
private static final int START_BACK_WIDTH = 12;
|
||||
private static final int START_BACK_HEIGHT = 7;
|
||||
|
||||
// Make the Guide Menu be in the center of START and BACK menu
|
||||
private static final int GUIDE_X = START_X-BACK_X;
|
||||
private static final int GUIDE_Y = START_BACK_Y;
|
||||
|
||||
public static void createDefaultLayout(final VirtualController controller, final Context context) {
|
||||
|
||||
DisplayMetrics screen = context.getResources().getDisplayMetrics();
|
||||
@@ -333,6 +337,16 @@ public class VirtualControllerConfigurationLoader {
|
||||
);
|
||||
}
|
||||
|
||||
if(config.showGuideButton){
|
||||
controller.addElement(createDigitalButton(VirtualControllerElement.EID_GDB,
|
||||
ControllerPacket.SPECIAL_BUTTON_FLAG, 0, 1, "GUIDE", -1, controller, context),
|
||||
screenScale(GUIDE_X, height)+ rightDisplacement,
|
||||
screenScale(GUIDE_Y, height),
|
||||
screenScale(START_BACK_WIDTH, height),
|
||||
screenScale(START_BACK_HEIGHT, height)
|
||||
);
|
||||
}
|
||||
|
||||
controller.setOpacity(config.oscOpacity);
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -35,6 +35,7 @@ public abstract class VirtualControllerElement extends View {
|
||||
public static final int EID_RS = 13;
|
||||
public static final int EID_LSB = 14;
|
||||
public static final int EID_RSB = 15;
|
||||
public static final int EID_GDB = 16;
|
||||
|
||||
protected VirtualController virtualController;
|
||||
protected final int elementId;
|
||||
|
||||
@@ -471,9 +471,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
||||
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, refreshRate);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Populate keys for adaptive playback
|
||||
if (adaptivePlayback) {
|
||||
videoFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, initialWidth);
|
||||
videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, initialHeight);
|
||||
}
|
||||
|
||||
@@ -596,45 +596,39 @@ public class MediaCodecHelper {
|
||||
|
||||
public static boolean decoderSupportsFusedIdrFrame(MediaCodecInfo decoderInfo, String mimeType) {
|
||||
// If adaptive playback is supported, we can submit new CSD together with a keyframe
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
if (decoderInfo.getCapabilitiesForType(mimeType).
|
||||
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
|
||||
{
|
||||
LimeLog.info("Decoder supports fused IDR frames (FEATURE_AdaptivePlayback)");
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Tolerate buggy codecs
|
||||
e.printStackTrace();
|
||||
try {
|
||||
if (decoderInfo.getCapabilitiesForType(mimeType).
|
||||
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
|
||||
LimeLog.info("Decoder supports fused IDR frames (FEATURE_AdaptivePlayback)");
|
||||
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())) {
|
||||
LimeLog.info("Decoder blacklisted for adaptive playback");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (decoderInfo.getCapabilitiesForType(mimeType).
|
||||
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
|
||||
{
|
||||
// This will make getCapabilities() return that adaptive playback is supported
|
||||
LimeLog.info("Adaptive playback supported (FEATURE_AdaptivePlayback)");
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Tolerate buggy codecs
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (isDecoderInList(blacklistedAdaptivePlaybackPrefixes, decoderInfo.getName())) {
|
||||
LimeLog.info("Decoder blacklisted for adaptive playback");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (decoderInfo.getCapabilitiesForType(mimeType).
|
||||
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
|
||||
{
|
||||
// This will make getCapabilities() return that adaptive playback is supported
|
||||
LimeLog.info("Adaptive playback supported (FEATURE_AdaptivePlayback)");
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Tolerate buggy codecs
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -709,13 +703,6 @@ public class MediaCodecHelper {
|
||||
}
|
||||
|
||||
public static boolean decoderIsWhitelistedForHevc(MediaCodecInfo decoderInfo) {
|
||||
// Google didn't have official support for HEVC (or more importantly, a CTS test) until
|
||||
// Lollipop. I've seen some MediaTek devices on 4.4 crash when attempting to use HEVC,
|
||||
// so I'm restricting HEVC usage to Lollipop and higher.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Software decoders are terrible and we never want to use them.
|
||||
// We want to catch decoders like:
|
||||
@@ -784,17 +771,10 @@ public class MediaCodecHelper {
|
||||
@SuppressLint("NewApi")
|
||||
private static LinkedList<MediaCodecInfo> getMediaCodecList() {
|
||||
LinkedList<MediaCodecInfo> infoList = new LinkedList<>();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
||||
Collections.addAll(infoList, mcl.getCodecInfos());
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
|
||||
infoList.add(MediaCodecList.getCodecInfoAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
||||
Collections.addAll(infoList, mcl.getCodecInfos());
|
||||
|
||||
return infoList;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,35 @@ public class ComputerDatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a computer by name
|
||||
* NOTE: It is perfectly valid for multiple computers to have the same name,
|
||||
* this function will only return the first one it finds.
|
||||
* Consider using getComputerByUUID instead.
|
||||
* @param name The name of the computer
|
||||
* @see ComputerDatabaseManager#getComputerByUUID(String) for alternative.
|
||||
* @return The computer details, or null if no computer with that name exists
|
||||
*/
|
||||
public ComputerDetails getComputerByName(String name) {
|
||||
try (final Cursor c = computerDb.query(
|
||||
COMPUTER_TABLE_NAME, null, COMPUTER_NAME_COLUMN_NAME+"=?",
|
||||
new String[]{ name }, null, null, null)
|
||||
) {
|
||||
if (!c.moveToFirst()) {
|
||||
// No matching computer
|
||||
return null;
|
||||
}
|
||||
|
||||
return getComputerFromCursor(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a computer by UUID
|
||||
* @param uuid The UUID of the computer
|
||||
* @see ComputerDatabaseManager#getComputerByName(String) for alternative.
|
||||
* @return The computer details, or null if no computer with that UUID exists
|
||||
*/
|
||||
public ComputerDetails getComputerByUUID(String uuid) {
|
||||
try (final Cursor c = computerDb.query(
|
||||
COMPUTER_TABLE_NAME, null, COMPUTER_UUID_COLUMN_NAME+"=?",
|
||||
|
||||
@@ -341,57 +341,53 @@ public class ComputerManagerService extends Service {
|
||||
// Acquire the default network lock since we could be changing global process state
|
||||
defaultNetworkLock.lock();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// On Lollipop or later, we can bind our process to the underlying interface
|
||||
// to ensure our STUN request goes out on that interface or not at all (which is
|
||||
// preferable to getting a VPN endpoint address back).
|
||||
Network[] networks = connMgr.getAllNetworks();
|
||||
for (Network net : networks) {
|
||||
NetworkCapabilities netCaps = connMgr.getNetworkCapabilities(net);
|
||||
if (netCaps != null) {
|
||||
if (!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) &&
|
||||
!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
|
||||
// This network looks like an underlying multicast-capable transport,
|
||||
// so let's guess that it's probably where our mDNS response came from.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (connMgr.bindProcessToNetwork(net)) {
|
||||
boundToNetwork = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ConnectivityManager.setProcessDefaultNetwork(net)) {
|
||||
// On Lollipop or later, we can bind our process to the underlying interface
|
||||
// to ensure our STUN request goes out on that interface or not at all (which is
|
||||
// preferable to getting a VPN endpoint address back).
|
||||
Network[] networks = connMgr.getAllNetworks();
|
||||
for (Network net : networks) {
|
||||
NetworkCapabilities netCaps = connMgr.getNetworkCapabilities(net);
|
||||
if (netCaps != null) {
|
||||
if (!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) &&
|
||||
!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
|
||||
// This network looks like an underlying multicast-capable transport,
|
||||
// so let's guess that it's probably where our mDNS response came from.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (connMgr.bindProcessToNetwork(net)) {
|
||||
boundToNetwork = true;
|
||||
break;
|
||||
}
|
||||
} else if (ConnectivityManager.setProcessDefaultNetwork(net)) {
|
||||
boundToNetwork = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the STUN request if we're not on a VPN or if we bound to a network
|
||||
if (!activeNetworkIsVpn || boundToNetwork) {
|
||||
String stunResolvedAddress = NvConnection.findExternalAddressForMdns("stun.moonlight-stream.org", 3478);
|
||||
if (stunResolvedAddress != null) {
|
||||
// We don't know for sure what the external port is, so we will have to guess.
|
||||
// When we contact the PC (if we haven't already), it will update the port.
|
||||
details.remoteAddress = new ComputerDetails.AddressTuple(stunResolvedAddress, details.guessExternalPort());
|
||||
// Perform the STUN request if we're not on a VPN or if we bound to a network
|
||||
if (!activeNetworkIsVpn || boundToNetwork) {
|
||||
String stunResolvedAddress = NvConnection.findExternalAddressForMdns("stun.moonlight-stream.org", 3478);
|
||||
if (stunResolvedAddress != null) {
|
||||
// We don't know for sure what the external port is, so we will have to guess.
|
||||
// When we contact the PC (if we haven't already), it will update the port.
|
||||
details.remoteAddress = new ComputerDetails.AddressTuple(stunResolvedAddress, details.guessExternalPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unbind from the network
|
||||
if (boundToNetwork) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
connMgr.bindProcessToNetwork(null);
|
||||
// Unbind from the network
|
||||
if (boundToNetwork) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
connMgr.bindProcessToNetwork(null);
|
||||
} else {
|
||||
ConnectivityManager.setProcessDefaultNetwork(null);
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
ConnectivityManager.setProcessDefaultNetwork(null);
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock the network state
|
||||
if (activeNetworkIsVpn) {
|
||||
defaultNetworkLock.unlock();
|
||||
// Unlock the network state
|
||||
if (activeNetworkIsVpn) {
|
||||
defaultNetworkLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,8 @@ public class DiskAssetLoader {
|
||||
|
||||
public DiskAssetLoader(Context context) {
|
||||
this.cacheDir = context.getCacheDir();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
this.isLowRamDevice =
|
||||
((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice();
|
||||
}
|
||||
else {
|
||||
// Use conservative low RAM behavior on very old devices
|
||||
this.isLowRamDevice = true;
|
||||
}
|
||||
this.isLowRamDevice =
|
||||
((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice();
|
||||
}
|
||||
|
||||
public boolean checkCacheExists(CachedAppAssetLoader.LoaderTuple tuple) {
|
||||
|
||||
@@ -198,7 +198,7 @@ public class NvConnection {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
else {
|
||||
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
|
||||
if (activeNetworkInfo != null) {
|
||||
switch (activeNetworkInfo.getType()) {
|
||||
|
||||
@@ -403,16 +403,7 @@ public class NvHTTP {
|
||||
try {
|
||||
SSLContext sc = SSLContext.getInstance("TLS");
|
||||
sc.init(new KeyManager[] { keyManager }, new TrustManager[] { trustManager }, new SecureRandom());
|
||||
|
||||
// TLS 1.2 is not enabled by default prior to Android 5.0, so we'll need a custom
|
||||
// SSLSocketFactory in order to connect to GFE 3.20.4 which requires TLSv1.2 or later.
|
||||
// We don't just always use TLSv12SocketFactory because explicitly specifying TLS versions
|
||||
// prevents later TLS versions from being negotiated even if client and server otherwise
|
||||
// support them.
|
||||
return client.newBuilder().sslSocketFactory(
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
sc.getSocketFactory() : new TLSv12SocketFactory(sc),
|
||||
trustManager).build();
|
||||
return client.newBuilder().sslSocketFactory(sc.getSocketFactory(), trustManager).build();
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -595,6 +586,12 @@ public class NvHTTP {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an app by ID
|
||||
* @param appId The ID of the app
|
||||
* @see #getAppByName(String) for alternative.
|
||||
* @return app details, or null if no app with that ID exists
|
||||
*/
|
||||
public NvApp getAppById(int appId) throws IOException, XmlPullParserException {
|
||||
LinkedList<NvApp> appList = getAppList();
|
||||
for (NvApp appFromList : appList) {
|
||||
@@ -604,11 +601,16 @@ public class NvHTTP {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* NOTE: Only use this function if you know what you're doing.
|
||||
* It's totally valid to have two apps named the same thing,
|
||||
* or even nothing at all! Look apps up by ID if at all possible
|
||||
* using the above function */
|
||||
|
||||
/**
|
||||
* Get an app by name
|
||||
* NOTE: It is perfectly valid for multiple apps to have the same name,
|
||||
* this function will only return the first one it finds.
|
||||
* Consider using getAppById instead.
|
||||
* @param appName The name of the app
|
||||
* @see #getAppById(int) for alternative.
|
||||
* @return app details, or null if no app with that name exists
|
||||
*/
|
||||
public NvApp getAppByName(String appName) throws IOException, XmlPullParserException {
|
||||
LinkedList<NvApp> appList = getAppList();
|
||||
for (NvApp appFromList : appList) {
|
||||
@@ -814,62 +816,4 @@ public class NvHTTP {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Based on example code from https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
|
||||
private static class TLSv12SocketFactory extends SSLSocketFactory {
|
||||
private SSLSocketFactory internalSSLSocketFactory;
|
||||
|
||||
public TLSv12SocketFactory(SSLContext context) {
|
||||
internalSSLSocketFactory = context.getSocketFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return internalSSLSocketFactory.getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return internalSSLSocketFactory.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
|
||||
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
|
||||
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
|
||||
}
|
||||
|
||||
private Socket enableTLSv12OnSocket(Socket socket) {
|
||||
if (socket instanceof SSLSocket) {
|
||||
// TLS 1.2 is not enabled by default prior to Android 5.0. We must enable it
|
||||
// explicitly to ensure we can communicate with GFE 3.20.4 which blocks TLS 1.0.
|
||||
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.limelight.R;
|
||||
import static com.limelight.binding.input.virtual_controller.VirtualControllerConfigurationLoader.OSC_PREFERENCE;
|
||||
|
||||
public class ConfirmDeleteOscPreference extends DialogPreference {
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public ConfirmDeleteOscPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
@@ -26,7 +25,6 @@ public class ConfirmDeleteOscPreference extends DialogPreference {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public ConfirmDeleteOscPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,10 @@ import android.provider.Settings;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class LanguagePreference extends ListPreference {
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public LanguagePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public LanguagePreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public class PreferenceConfiguration {
|
||||
private static final String VIDEO_FORMAT_PREF_STRING = "video_format";
|
||||
private static final String ONSCREEN_CONTROLLER_PREF_STRING = "checkbox_show_onscreen_controls";
|
||||
private static final String ONLY_L3_R3_PREF_STRING = "checkbox_only_show_L3R3";
|
||||
private static final String SHOW_GUIDE_BUTTON_PREF_STRING = "checkbox_show_guide_button";
|
||||
private static final String LEGACY_DISABLE_FRAME_DROP_PREF_STRING = "checkbox_disable_frame_drop";
|
||||
private static final String ENABLE_HDR_PREF_STRING = "checkbox_enable_hdr";
|
||||
private static final String ENABLE_PIP_PREF_STRING = "checkbox_enable_pip";
|
||||
@@ -83,6 +84,7 @@ public class PreferenceConfiguration {
|
||||
|
||||
private static final boolean ONSCREEN_CONTROLLER_DEFAULT = false;
|
||||
private static final boolean ONLY_L3_R3_DEFAULT = false;
|
||||
private static final boolean SHOW_GUIDE_BUTTON_DEFAULT = true;
|
||||
private static final boolean DEFAULT_ENABLE_HDR = false;
|
||||
private static final boolean DEFAULT_ENABLE_PIP = false;
|
||||
private static final boolean DEFAULT_ENABLE_PERF_OVERLAY = false;
|
||||
@@ -130,6 +132,7 @@ public class PreferenceConfiguration {
|
||||
public boolean smallIconMode, multiController, usbDriver, flipFaceButtons;
|
||||
public boolean onscreenController;
|
||||
public boolean onlyL3R3;
|
||||
public boolean showGuideButton;
|
||||
public boolean enableHdr;
|
||||
public boolean enablePip;
|
||||
public boolean enablePerfOverlay;
|
||||
@@ -577,6 +580,7 @@ public class PreferenceConfiguration {
|
||||
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);
|
||||
config.showGuideButton = prefs.getBoolean(SHOW_GUIDE_BUTTON_PREF_STRING, SHOW_GUIDE_BUTTON_DEFAULT);
|
||||
config.enableHdr = prefs.getBoolean(ENABLE_HDR_PREF_STRING, DEFAULT_ENABLE_HDR) && !isShieldAtvFirmwareWithBrokenHdr();
|
||||
config.enablePip = prefs.getBoolean(ENABLE_PIP_PREF_STRING, DEFAULT_ENABLE_PIP);
|
||||
config.enablePerfOverlay = prefs.getBoolean(ENABLE_PERF_OVERLAY_STRING, DEFAULT_ENABLE_PERF_OVERLAY);
|
||||
|
||||
@@ -511,29 +511,15 @@ public class StreamSettings extends Activity {
|
||||
// Never remove 720p
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
// On Android 4.2 and later, we can get the true metrics via the
|
||||
// getRealMetrics() function (unlike the lies that getWidth() and getHeight()
|
||||
// tell to us).
|
||||
else {
|
||||
// We can get the true metrics via the getRealMetrics() function (unlike the lies
|
||||
// that getWidth() and getHeight() tell to us).
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
display.getRealMetrics(metrics);
|
||||
int width = Math.max(metrics.widthPixels, metrics.heightPixels);
|
||||
int height = Math.min(metrics.widthPixels, metrics.heightPixels);
|
||||
addNativeResolutionEntries(width, height, false);
|
||||
}
|
||||
else {
|
||||
// On Android 4.1, we have to resort to reflection to invoke hidden APIs
|
||||
// to get the real screen dimensions.
|
||||
try {
|
||||
Method getRawHeightFunc = Display.class.getMethod("getRawHeight");
|
||||
Method getRawWidthFunc = Display.class.getMethod("getRawWidth");
|
||||
int width = (Integer) getRawWidthFunc.invoke(display);
|
||||
int height = (Integer) getRawHeightFunc.invoke(display);
|
||||
addNativeResolutionEntries(Math.max(width, height), Math.min(width, height), false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!PreferenceConfiguration.readPreferences(this.getActivity()).unlockFps) {
|
||||
// We give some extra room in case the FPS is rounded down
|
||||
@@ -562,49 +548,29 @@ public class StreamSettings extends Activity {
|
||||
}
|
||||
addNativeFrameRateEntry(maxSupportedFps);
|
||||
|
||||
// Android L introduces proper 7.1 surround sound support. Remove the 7.1 option
|
||||
// for earlier versions of Android to prevent AudioTrack initialization issues.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
LimeLog.info("Excluding 7.1 surround sound option based on OS");
|
||||
removeValue(PreferenceConfiguration.AUDIO_CONFIG_PREF_STRING, "71", new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setValue(PreferenceConfiguration.AUDIO_CONFIG_PREF_STRING, "51");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Android L introduces the drop duplicate behavior of releaseOutputBuffer()
|
||||
// that the unlock FPS option relies on to not massively increase latency.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
LimeLog.info("Excluding unlock FPS toggle based on OS");
|
||||
PreferenceCategory category =
|
||||
(PreferenceCategory) findPreference("category_advanced_settings");
|
||||
category.removePreference(findPreference("checkbox_unlock_fps"));
|
||||
}
|
||||
else {
|
||||
findPreference(PreferenceConfiguration.UNLOCK_FPS_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
// HACK: We need to let the preference change succeed before reinitializing to ensure
|
||||
// it's reflected in the new layout.
|
||||
final Handler h = new Handler();
|
||||
h.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Ensure the activity is still open when this timeout expires
|
||||
StreamSettings settingsActivity = (StreamSettings)SettingsFragment.this.getActivity();
|
||||
if (settingsActivity != null) {
|
||||
settingsActivity.reloadSettings();
|
||||
}
|
||||
findPreference(PreferenceConfiguration.UNLOCK_FPS_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
// HACK: We need to let the preference change succeed before reinitializing to ensure
|
||||
// it's reflected in the new layout.
|
||||
final Handler h = new Handler();
|
||||
h.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Ensure the activity is still open when this timeout expires
|
||||
StreamSettings settingsActivity = (StreamSettings) SettingsFragment.this.getActivity();
|
||||
if (settingsActivity != null) {
|
||||
settingsActivity.reloadSettings();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// Allow the original preference change to take place
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// Allow the original preference change to take place
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Remove HDR preference for devices below Nougat
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
@@ -623,6 +589,7 @@ public class StreamSettings extends Activity {
|
||||
for (int hdrType : hdrCaps.getSupportedHdrTypes()) {
|
||||
if (hdrType == Display.HdrCapabilities.HDR_TYPE_HDR10) {
|
||||
foundHdr10 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ public class WebLauncherPreference extends Preference {
|
||||
initialize(attrs);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public WebLauncherPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize(attrs);
|
||||
|
||||
@@ -30,6 +30,6 @@ public class AdapterFragment extends Fragment {
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
callbacks.receiveAbsListView((AbsListView) getView().findViewById(R.id.fragmentView));
|
||||
callbacks.receiveAbsListView(getView().findViewById(R.id.fragmentView));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public class StreamView extends SurfaceView {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
public StreamView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class NetHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
else {
|
||||
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
|
||||
if (activeNetworkInfo != null) {
|
||||
return activeNetworkInfo.getType() == ConnectivityManager.TYPE_VPN;
|
||||
|
||||
@@ -2,15 +2,12 @@ package com.limelight.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Build;
|
||||
|
||||
import com.limelight.AppView;
|
||||
import com.limelight.ShortcutTrampoline;
|
||||
import com.limelight.R;
|
||||
import com.limelight.nvstream.http.ComputerDetails;
|
||||
import com.limelight.nvstream.http.NvApp;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# Application.mk for Moonlight
|
||||
|
||||
# Our minimum version is Android 4.1
|
||||
APP_PLATFORM := android-16
|
||||
# Our minimum version is Android 5.0
|
||||
APP_PLATFORM := android-21
|
||||
|
||||
# We support 16KB pages
|
||||
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
|
||||
|
||||
@@ -58,6 +58,8 @@ LOCAL_LDLIBS := -llog
|
||||
LOCAL_STATIC_LIBRARIES := libopus libssl libcrypto cpufeatures
|
||||
LOCAL_LDFLAGS += -Wl,--exclude-libs,ALL
|
||||
|
||||
LOCAL_BRANCH_PROTECTION := standard
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,android/cpufeatures)
|
||||
@@ -0,0 +1 @@
|
||||
Static libraries were built from https://github.com/cgutman/moonlight-mobile-deps using AppVeyor CI
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,100 +0,0 @@
|
||||
ANDROID_API_TARGET=21
|
||||
PARALLEL_JOBS=$(nproc)
|
||||
|
||||
rm -r ./android
|
||||
mkdir android
|
||||
|
||||
function build_one
|
||||
{
|
||||
PREFIX=$(pwd)/android/$CPU
|
||||
SYSROOT=$NDK/platforms/android-$ANDROID_API_TARGET/arch-$SYSROOT_CPU
|
||||
TOOLCHAIN_PATH=$NDK/toolchains/$TOOLCHAIN_DIR/prebuilt/linux-x86_64
|
||||
export PATH=$PATH:$TOOLCHAIN_PATH/bin
|
||||
./configure \
|
||||
--build=x86_64-unknown-linux-gnu \
|
||||
--host=$TOOLCHAIN_BIN_PREFIX \
|
||||
--target=$TOOLCHAIN_BIN_PREFIX \
|
||||
CFLAGS="--sysroot=$SYSROOT -O2 $ADDI_CFLAGS" \
|
||||
$ADDI_CONFIGURE_FLAGS
|
||||
make clean
|
||||
make -j$PARALLEL_JOBS
|
||||
mkdir android/$CPU
|
||||
cp .libs/libopus.a android/$CPU
|
||||
}
|
||||
|
||||
function build_mips
|
||||
{
|
||||
CPU=mips
|
||||
SYSROOT_CPU=mips
|
||||
TOOLCHAIN_BIN_PREFIX=mipsel-linux-android
|
||||
TOOLCHAIN_DIR=mipsel-linux-android-4.9
|
||||
ADDI_CFLAGS="-mips32 -mhard-float -EL -mno-dsp"
|
||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point" # fixed point
|
||||
build_one
|
||||
}
|
||||
|
||||
function build_mips64
|
||||
{
|
||||
CPU=mips64
|
||||
SYSROOT_CPU=mips64
|
||||
TOOLCHAIN_BIN_PREFIX=mips64el-linux-android
|
||||
TOOLCHAIN_DIR=mips64el-linux-android-4.9
|
||||
ADDI_CFLAGS="-mips64r6"
|
||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point" # fixed point
|
||||
build_one
|
||||
}
|
||||
|
||||
function build_x86
|
||||
{
|
||||
CPU=x86
|
||||
SYSROOT_CPU=x86
|
||||
TOOLCHAIN_BIN_PREFIX=i686-linux-android
|
||||
TOOLCHAIN_DIR=x86-4.9
|
||||
ADDI_CFLAGS="-march=i686 -mtune=atom -mstackrealign -msse -msse2 -msse3 -mssse3 -mfpmath=sse -m32"
|
||||
ADDI_CONFIGURE_FLAGS="" # floating point for SSE optimizations
|
||||
build_one
|
||||
}
|
||||
|
||||
function build_x86_64
|
||||
{
|
||||
CPU=x86_64
|
||||
SYSROOT_CPU=x86_64
|
||||
TOOLCHAIN_BIN_PREFIX=x86_64-linux-android
|
||||
TOOLCHAIN_DIR=x86_64-4.9
|
||||
ADDI_CFLAGS="-msse -msse2 -msse3 -mssse3 -msse4 -msse4.1 -msse4.2 -mpopcnt -m64"
|
||||
ADDI_CONFIGURE_FLAGS="" # floating point for SSE optimizations
|
||||
build_one
|
||||
}
|
||||
|
||||
function build_armv7
|
||||
{
|
||||
CPU=arm
|
||||
SYSROOT_CPU=arm
|
||||
TOOLCHAIN_BIN_PREFIX=arm-linux-androideabi
|
||||
TOOLCHAIN_DIR=arm-linux-androideabi-4.9
|
||||
ADDI_CFLAGS="-marm -mfpu=vfpv3-d16"
|
||||
ADDI_LDFLAGS=""
|
||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point" # fixed point for NEON, EDSP, Media
|
||||
build_one
|
||||
}
|
||||
|
||||
# ARMv8 doesn't currently have assembly in the opus project. We still use fixed point
|
||||
# anyway in the hopes that it will be more performant even without assembly.
|
||||
function build_armv8
|
||||
{
|
||||
CPU=aarch64
|
||||
SYSROOT_CPU=arm64
|
||||
TOOLCHAIN_BIN_PREFIX=aarch64-linux-android
|
||||
TOOLCHAIN_DIR=aarch64-linux-android-4.9
|
||||
ADDI_CFLAGS=""
|
||||
ADDI_LDFLAGS=""
|
||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point"
|
||||
build_one
|
||||
}
|
||||
|
||||
build_mips
|
||||
build_mips64
|
||||
build_x86
|
||||
build_x86_64
|
||||
build_armv7
|
||||
build_armv8
|
||||
@@ -103,7 +103,7 @@ extern "C" {
|
||||
* @endcode
|
||||
*
|
||||
* where opus_encoder_get_size() returns the required size for the encoder state. Note that
|
||||
* future versions of this code may change the size, so no assuptions should be made about it.
|
||||
* future versions of this code may change the size, so no assumptions should be made about it.
|
||||
*
|
||||
* The encoder state is always continuous in memory and only a shallow copy is sufficient
|
||||
* to copy it (e.g. memcpy())
|
||||
@@ -198,7 +198,7 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_encoder_get_size(int channels);
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
||||
* @param [in] application <tt>int</tt>: Coding mode (@ref OPUS_APPLICATION_VOIP/@ref OPUS_APPLICATION_AUDIO/@ref OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
* @param [in] application <tt>int</tt>: Coding mode (one of @ref OPUS_APPLICATION_VOIP, @ref OPUS_APPLICATION_AUDIO, or @ref OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
* @param [out] error <tt>int*</tt>: @ref opus_errorcodes
|
||||
* @note Regardless of the sampling rate and number channels selected, the Opus encoder
|
||||
* can switch to a lower audio bandwidth or number of channels if the bitrate
|
||||
@@ -222,7 +222,7 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusEncoder *opus_encoder_create(
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
||||
* @param [in] application <tt>int</tt>: Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO/OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
* @param [in] application <tt>int</tt>: Coding mode (one of OPUS_APPLICATION_VOIP, OPUS_APPLICATION_AUDIO, or OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT int opus_encoder_init(
|
||||
@@ -357,7 +357,7 @@ OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...) OPUS_ARG_NON
|
||||
* error = opus_decoder_init(dec, Fs, channels);
|
||||
* @endcode
|
||||
* where opus_decoder_get_size() returns the required size for the decoder state. Note that
|
||||
* future versions of this code may change the size, so no assuptions should be made about it.
|
||||
* future versions of this code may change the size, so no assumptions should be made about it.
|
||||
*
|
||||
* The decoder state is always continuous in memory and only a shallow copy is sufficient
|
||||
* to copy it (e.g. memcpy())
|
||||
@@ -398,6 +398,21 @@ OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...) OPUS_ARG_NON
|
||||
*/
|
||||
typedef struct OpusDecoder OpusDecoder;
|
||||
|
||||
/** Opus DRED decoder.
|
||||
* This contains the complete state of an Opus DRED decoder.
|
||||
* It is position independent and can be freely copied.
|
||||
* @see opus_dred_decoder_create,opus_dred_decoder_init
|
||||
*/
|
||||
typedef struct OpusDREDDecoder OpusDREDDecoder;
|
||||
|
||||
|
||||
/** Opus DRED state.
|
||||
* This contains the complete state of an Opus DRED packet.
|
||||
* It is position independent and can be freely copied.
|
||||
* @see opus_dred_create,opus_dred_init
|
||||
*/
|
||||
typedef struct OpusDRED OpusDRED;
|
||||
|
||||
/** Gets the size of an <code>OpusDecoder</code> structure.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels.
|
||||
* This must be 1 or 2.
|
||||
@@ -511,6 +526,101 @@ OPUS_EXPORT int opus_decoder_ctl(OpusDecoder *st, int request, ...) OPUS_ARG_NON
|
||||
*/
|
||||
OPUS_EXPORT void opus_decoder_destroy(OpusDecoder *st);
|
||||
|
||||
/** Gets the size of an <code>OpusDREDDecoder</code> structure.
|
||||
* @returns The size in bytes.
|
||||
*/
|
||||
OPUS_EXPORT int opus_dred_decoder_get_size(void);
|
||||
|
||||
/** Allocates and initializes an OpusDREDDecoder state.
|
||||
* @param [out] error <tt>int*</tt>: #OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT OpusDREDDecoder *opus_dred_decoder_create(int *error);
|
||||
|
||||
/** Initializes an <code>OpusDREDDecoder</code> state.
|
||||
* @param[in] dec <tt>OpusDREDDecoder*</tt>: State to be initialized.
|
||||
*/
|
||||
OPUS_EXPORT int opus_dred_decoder_init(OpusDREDDecoder *dec);
|
||||
|
||||
/** Frees an <code>OpusDREDDecoder</code> allocated by opus_dred_decoder_create().
|
||||
* @param[in] dec <tt>OpusDREDDecoder*</tt>: State to be freed.
|
||||
*/
|
||||
OPUS_EXPORT void opus_dred_decoder_destroy(OpusDREDDecoder *dec);
|
||||
|
||||
/** Perform a CTL function on an Opus DRED decoder.
|
||||
*
|
||||
* Generally the request and subsequent arguments are generated
|
||||
* by a convenience macro.
|
||||
* @param dred_dec <tt>OpusDREDDecoder*</tt>: DRED Decoder state.
|
||||
* @param request This and all remaining parameters should be replaced by one
|
||||
* of the convenience macros in @ref opus_genericctls or
|
||||
* @ref opus_decoderctls.
|
||||
* @see opus_genericctls
|
||||
* @see opus_decoderctls
|
||||
*/
|
||||
OPUS_EXPORT int opus_dred_decoder_ctl(OpusDREDDecoder *dred_dec, int request, ...);
|
||||
|
||||
/** Gets the size of an <code>OpusDRED</code> structure.
|
||||
* @returns The size in bytes.
|
||||
*/
|
||||
OPUS_EXPORT int opus_dred_get_size(void);
|
||||
|
||||
/** Allocates and initializes a DRED state.
|
||||
* @param [out] error <tt>int*</tt>: #OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT OpusDRED *opus_dred_alloc(int *error);
|
||||
|
||||
/** Frees an <code>OpusDRED</code> allocated by opus_dred_create().
|
||||
* @param[in] dec <tt>OpusDRED*</tt>: State to be freed.
|
||||
*/
|
||||
OPUS_EXPORT void opus_dred_free(OpusDRED *dec);
|
||||
|
||||
/** Decode an Opus DRED packet.
|
||||
* @param [in] dred_dec <tt>OpusDRED*</tt>: DRED Decoder state
|
||||
* @param [in] dred <tt>OpusDRED*</tt>: DRED state
|
||||
* @param [in] data <tt>char*</tt>: Input payload
|
||||
* @param [in] len <tt>opus_int32</tt>: Number of bytes in payload
|
||||
* @param [in] max_dred_samples <tt>opus_int32</tt>: Maximum number of DRED samples that may be needed (if available in the packet).
|
||||
* @param [in] sampling_rate <tt>opus_int32</tt>: Sampling rate used for max_dred_samples argument. Needs not match the actual sampling rate of the decoder.
|
||||
* @param [out] dred_end <tt>opus_int32*</tt>: Number of non-encoded (silence) samples between the DRED timestamp and the last DRED sample.
|
||||
* @param [in] defer_processing <tt>int</tt>: Flag (0 or 1). If set to one, the CPU-intensive part of the DRED decoding is deferred until opus_dred_process() is called.
|
||||
* @returns Offset (positive) of the first decoded DRED samples, zero if no DRED is present, or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT int opus_dred_parse(OpusDREDDecoder *dred_dec, OpusDRED *dred, const unsigned char *data, opus_int32 len, opus_int32 max_dred_samples, opus_int32 sampling_rate, int *dred_end, int defer_processing) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Finish decoding an Opus DRED packet. The function only needs to be called if opus_dred_parse() was called with defer_processing=1.
|
||||
* The source and destination will often be the same DRED state.
|
||||
* @param [in] dred_dec <tt>OpusDRED*</tt>: DRED Decoder state
|
||||
* @param [in] src <tt>OpusDRED*</tt>: Source DRED state to start the processing from.
|
||||
* @param [out] dst <tt>OpusDRED*</tt>: Destination DRED state to store the updated state after processing.
|
||||
* @returns @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT int opus_dred_process(OpusDREDDecoder *dred_dec, const OpusDRED *src, OpusDRED *dst);
|
||||
|
||||
/** Decode audio from an Opus DRED packet with floating point output.
|
||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
||||
* @param [in] dred <tt>OpusDRED*</tt>: DRED state
|
||||
* @param [in] dred_offset <tt>opus_int32</tt>: position of the redundancy to decode (in samples before the beginning of the real audio data in the packet).
|
||||
* @param [out] pcm <tt>opus_int16*</tt>: Output signal (interleaved if 2 channels). length
|
||||
* is frame_size*channels*sizeof(opus_int16)
|
||||
* @param [in] frame_size Number of samples per channel to decode in \a pcm.
|
||||
* frame_size <b>must</b> be a multiple of 2.5 ms.
|
||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT int opus_decoder_dred_decode(OpusDecoder *st, const OpusDRED *dred, opus_int32 dred_offset, opus_int16 *pcm, opus_int32 frame_size);
|
||||
|
||||
/** Decode audio from an Opus DRED packet with floating point output.
|
||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
||||
* @param [in] dred <tt>OpusDRED*</tt>: DRED state
|
||||
* @param [in] dred_offset <tt>opus_int32</tt>: position of the redundancy to decode (in samples before the beginning of the real audio data in the packet).
|
||||
* @param [out] pcm <tt>float*</tt>: Output signal (interleaved if 2 channels). length
|
||||
* is frame_size*channels*sizeof(float)
|
||||
* @param [in] frame_size Number of samples per channel to decode in \a pcm.
|
||||
* frame_size <b>must</b> be a multiple of 2.5 ms.
|
||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT int opus_decoder_dred_decode_float(OpusDecoder *st, const OpusDRED *dred, opus_int32 dred_offset, float *pcm, opus_int32 frame_size);
|
||||
|
||||
|
||||
/** Parse an opus packet into one or more frames.
|
||||
* Opus_decode will perform this operation internally so most applications do
|
||||
* not need to use this function.
|
||||
@@ -583,6 +693,14 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_frames(const unsigned
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Checks whether an Opus packet has LBRR.
|
||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
||||
* @returns 1 is LBRR is present, 0 otherwise
|
||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_has_lbrr(const unsigned char packet[], opus_int32 len);
|
||||
|
||||
/** Gets the number of samples of an Opus packet.
|
||||
* @param [in] dec <tt>OpusDecoder*</tt>: Decoder state
|
||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||
|
||||
@@ -1,342 +0,0 @@
|
||||
/* Copyright (c) 2007-2008 CSIRO
|
||||
Copyright (c) 2007-2009 Xiph.Org Foundation
|
||||
Copyright (c) 2008-2012 Gregory Maxwell
|
||||
Written by Jean-Marc Valin and Gregory Maxwell */
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@file opus_custom.h
|
||||
@brief Opus-Custom reference implementation API
|
||||
*/
|
||||
|
||||
#ifndef OPUS_CUSTOM_H
|
||||
#define OPUS_CUSTOM_H
|
||||
|
||||
#include "opus_defines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_MODES
|
||||
# define OPUS_CUSTOM_EXPORT OPUS_EXPORT
|
||||
# define OPUS_CUSTOM_EXPORT_STATIC OPUS_EXPORT
|
||||
#else
|
||||
# define OPUS_CUSTOM_EXPORT
|
||||
# ifdef OPUS_BUILD
|
||||
# define OPUS_CUSTOM_EXPORT_STATIC static OPUS_INLINE
|
||||
# else
|
||||
# define OPUS_CUSTOM_EXPORT_STATIC
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** @defgroup opus_custom Opus Custom
|
||||
* @{
|
||||
* Opus Custom is an optional part of the Opus specification and
|
||||
* reference implementation which uses a distinct API from the regular
|
||||
* API and supports frame sizes that are not normally supported.\ Use
|
||||
* of Opus Custom is discouraged for all but very special applications
|
||||
* for which a frame size different from 2.5, 5, 10, or 20 ms is needed
|
||||
* (for either complexity or latency reasons) and where interoperability
|
||||
* is less important.
|
||||
*
|
||||
* In addition to the interoperability limitations the use of Opus custom
|
||||
* disables a substantial chunk of the codec and generally lowers the
|
||||
* quality available at a given bitrate. Normally when an application needs
|
||||
* a different frame size from the codec it should buffer to match the
|
||||
* sizes but this adds a small amount of delay which may be important
|
||||
* in some very low latency applications. Some transports (especially
|
||||
* constant rate RF transports) may also work best with frames of
|
||||
* particular durations.
|
||||
*
|
||||
* Libopus only supports custom modes if they are enabled at compile time.
|
||||
*
|
||||
* The Opus Custom API is similar to the regular API but the
|
||||
* @ref opus_encoder_create and @ref opus_decoder_create calls take
|
||||
* an additional mode parameter which is a structure produced by
|
||||
* a call to @ref opus_custom_mode_create. Both the encoder and decoder
|
||||
* must create a mode using the same sample rate (fs) and frame size
|
||||
* (frame size) so these parameters must either be signaled out of band
|
||||
* or fixed in a particular implementation.
|
||||
*
|
||||
* Similar to regular Opus the custom modes support on the fly frame size
|
||||
* switching, but the sizes available depend on the particular frame size in
|
||||
* use. For some initial frame sizes on a single on the fly size is available.
|
||||
*/
|
||||
|
||||
/** Contains the state of an encoder. One encoder state is needed
|
||||
for each stream. It is initialized once at the beginning of the
|
||||
stream. Do *not* re-initialize the state for every frame.
|
||||
@brief Encoder state
|
||||
*/
|
||||
typedef struct OpusCustomEncoder OpusCustomEncoder;
|
||||
|
||||
/** State of the decoder. One decoder state is needed for each stream.
|
||||
It is initialized once at the beginning of the stream. Do *not*
|
||||
re-initialize the state for every frame.
|
||||
@brief Decoder state
|
||||
*/
|
||||
typedef struct OpusCustomDecoder OpusCustomDecoder;
|
||||
|
||||
/** The mode contains all the information necessary to create an
|
||||
encoder. Both the encoder and decoder need to be initialized
|
||||
with exactly the same mode, otherwise the output will be
|
||||
corrupted.
|
||||
@brief Mode configuration
|
||||
*/
|
||||
typedef struct OpusCustomMode OpusCustomMode;
|
||||
|
||||
/** Creates a new mode struct. This will be passed to an encoder or
|
||||
* decoder. The mode MUST NOT BE DESTROYED until the encoders and
|
||||
* decoders that use it are destroyed as well.
|
||||
* @param [in] Fs <tt>int</tt>: Sampling rate (8000 to 96000 Hz)
|
||||
* @param [in] frame_size <tt>int</tt>: Number of samples (per channel) to encode in each
|
||||
* packet (64 - 1024, prime factorization must contain zero or more 2s, 3s, or 5s and no other primes)
|
||||
* @param [out] error <tt>int*</tt>: Returned error code (if NULL, no error will be returned)
|
||||
* @return A newly created mode
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomMode *opus_custom_mode_create(opus_int32 Fs, int frame_size, int *error);
|
||||
|
||||
/** Destroys a mode struct. Only call this after all encoders and
|
||||
* decoders using this mode are destroyed as well.
|
||||
* @param [in] mode <tt>OpusCustomMode*</tt>: Mode to be freed.
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT void opus_custom_mode_destroy(OpusCustomMode *mode);
|
||||
|
||||
|
||||
#if !defined(OPUS_BUILD) || defined(CELT_ENCODER_C)
|
||||
|
||||
/* Encoder */
|
||||
/** Gets the size of an OpusCustomEncoder structure.
|
||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Mode configuration
|
||||
* @param [in] channels <tt>int</tt>: Number of channels
|
||||
* @returns size
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_encoder_get_size(
|
||||
const OpusCustomMode *mode,
|
||||
int channels
|
||||
) OPUS_ARG_NONNULL(1);
|
||||
|
||||
# ifdef CUSTOM_MODES
|
||||
/** Initializes a previously allocated encoder state
|
||||
* The memory pointed to by st must be the size returned by opus_custom_encoder_get_size.
|
||||
* This is intended for applications which use their own allocator instead of malloc.
|
||||
* @see opus_custom_encoder_create(),opus_custom_encoder_get_size()
|
||||
* To reset a previously initialized state use the OPUS_RESET_STATE CTL.
|
||||
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Contains all the information about the characteristics of
|
||||
* the stream (must be the same characteristics as used for the
|
||||
* decoder)
|
||||
* @param [in] channels <tt>int</tt>: Number of channels
|
||||
* @return OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT int opus_custom_encoder_init(
|
||||
OpusCustomEncoder *st,
|
||||
const OpusCustomMode *mode,
|
||||
int channels
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Creates a new encoder state. Each stream needs its own encoder
|
||||
* state (can't be shared across simultaneous streams).
|
||||
* @param [in] mode <tt>OpusCustomMode*</tt>: Contains all the information about the characteristics of
|
||||
* the stream (must be the same characteristics as used for the
|
||||
* decoder)
|
||||
* @param [in] channels <tt>int</tt>: Number of channels
|
||||
* @param [out] error <tt>int*</tt>: Returns an error code
|
||||
* @return Newly created encoder state.
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomEncoder *opus_custom_encoder_create(
|
||||
const OpusCustomMode *mode,
|
||||
int channels,
|
||||
int *error
|
||||
) OPUS_ARG_NONNULL(1);
|
||||
|
||||
|
||||
/** Destroys a an encoder state.
|
||||
* @param[in] st <tt>OpusCustomEncoder*</tt>: State to be freed.
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT void opus_custom_encoder_destroy(OpusCustomEncoder *st);
|
||||
|
||||
/** Encodes a frame of audio.
|
||||
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
||||
* @param [in] pcm <tt>float*</tt>: PCM audio in float format, with a normal range of +/-1.0.
|
||||
* Samples with a range beyond +/-1.0 are supported but will
|
||||
* be clipped by decoders using the integer API and should
|
||||
* only be used if it is known that the far end supports
|
||||
* extended dynamic range. There must be exactly
|
||||
* frame_size samples per channel.
|
||||
* @param [in] frame_size <tt>int</tt>: Number of samples per frame of input signal
|
||||
* @param [out] compressed <tt>char *</tt>: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long.
|
||||
* @param [in] maxCompressedBytes <tt>int</tt>: Maximum number of bytes to use for compressing the frame
|
||||
* (can change from one frame to another)
|
||||
* @return Number of bytes written to "compressed".
|
||||
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
||||
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
||||
* decoding is possible.
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode_float(
|
||||
OpusCustomEncoder *st,
|
||||
const float *pcm,
|
||||
int frame_size,
|
||||
unsigned char *compressed,
|
||||
int maxCompressedBytes
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Encodes a frame of audio.
|
||||
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
||||
* @param [in] pcm <tt>opus_int16*</tt>: PCM audio in signed 16-bit format (native endian).
|
||||
* There must be exactly frame_size samples per channel.
|
||||
* @param [in] frame_size <tt>int</tt>: Number of samples per frame of input signal
|
||||
* @param [out] compressed <tt>char *</tt>: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long.
|
||||
* @param [in] maxCompressedBytes <tt>int</tt>: Maximum number of bytes to use for compressing the frame
|
||||
* (can change from one frame to another)
|
||||
* @return Number of bytes written to "compressed".
|
||||
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
||||
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
||||
* decoding is possible.
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode(
|
||||
OpusCustomEncoder *st,
|
||||
const opus_int16 *pcm,
|
||||
int frame_size,
|
||||
unsigned char *compressed,
|
||||
int maxCompressedBytes
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Perform a CTL function on an Opus custom encoder.
|
||||
*
|
||||
* Generally the request and subsequent arguments are generated
|
||||
* by a convenience macro.
|
||||
* @see opus_encoderctls
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT int opus_custom_encoder_ctl(OpusCustomEncoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||
|
||||
|
||||
#if !defined(OPUS_BUILD) || defined(CELT_DECODER_C)
|
||||
/* Decoder */
|
||||
|
||||
/** Gets the size of an OpusCustomDecoder structure.
|
||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Mode configuration
|
||||
* @param [in] channels <tt>int</tt>: Number of channels
|
||||
* @returns size
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_decoder_get_size(
|
||||
const OpusCustomMode *mode,
|
||||
int channels
|
||||
) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Initializes a previously allocated decoder state
|
||||
* The memory pointed to by st must be the size returned by opus_custom_decoder_get_size.
|
||||
* This is intended for applications which use their own allocator instead of malloc.
|
||||
* @see opus_custom_decoder_create(),opus_custom_decoder_get_size()
|
||||
* To reset a previously initialized state use the OPUS_RESET_STATE CTL.
|
||||
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Contains all the information about the characteristics of
|
||||
* the stream (must be the same characteristics as used for the
|
||||
* encoder)
|
||||
* @param [in] channels <tt>int</tt>: Number of channels
|
||||
* @return OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT_STATIC int opus_custom_decoder_init(
|
||||
OpusCustomDecoder *st,
|
||||
const OpusCustomMode *mode,
|
||||
int channels
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/** Creates a new decoder state. Each stream needs its own decoder state (can't
|
||||
* be shared across simultaneous streams).
|
||||
* @param [in] mode <tt>OpusCustomMode</tt>: Contains all the information about the characteristics of the
|
||||
* stream (must be the same characteristics as used for the encoder)
|
||||
* @param [in] channels <tt>int</tt>: Number of channels
|
||||
* @param [out] error <tt>int*</tt>: Returns an error code
|
||||
* @return Newly created decoder state.
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomDecoder *opus_custom_decoder_create(
|
||||
const OpusCustomMode *mode,
|
||||
int channels,
|
||||
int *error
|
||||
) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Destroys a an decoder state.
|
||||
* @param[in] st <tt>OpusCustomDecoder*</tt>: State to be freed.
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT void opus_custom_decoder_destroy(OpusCustomDecoder *st);
|
||||
|
||||
/** Decode an opus custom frame with floating point output
|
||||
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||
* @param [in] len <tt>int</tt>: Number of bytes in payload
|
||||
* @param [out] pcm <tt>float*</tt>: Output signal (interleaved if 2 channels). length
|
||||
* is frame_size*channels*sizeof(float)
|
||||
* @param [in] frame_size Number of samples per channel of available space in *pcm.
|
||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode_float(
|
||||
OpusCustomDecoder *st,
|
||||
const unsigned char *data,
|
||||
int len,
|
||||
float *pcm,
|
||||
int frame_size
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Decode an opus custom frame
|
||||
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||
* @param [in] len <tt>int</tt>: Number of bytes in payload
|
||||
* @param [out] pcm <tt>opus_int16*</tt>: Output signal (interleaved if 2 channels). length
|
||||
* is frame_size*channels*sizeof(opus_int16)
|
||||
* @param [in] frame_size Number of samples per channel of available space in *pcm.
|
||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode(
|
||||
OpusCustomDecoder *st,
|
||||
const unsigned char *data,
|
||||
int len,
|
||||
opus_int16 *pcm,
|
||||
int frame_size
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Perform a CTL function on an Opus custom decoder.
|
||||
*
|
||||
* Generally the request and subsequent arguments are generated
|
||||
* by a convenience macro.
|
||||
* @see opus_genericctls
|
||||
*/
|
||||
OPUS_CUSTOM_EXPORT int opus_custom_decoder_ctl(OpusCustomDecoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPUS_CUSTOM_H */
|
||||
@@ -64,7 +64,7 @@ extern "C" {
|
||||
/**Export control for opus functions */
|
||||
|
||||
#ifndef OPUS_EXPORT
|
||||
# if defined(WIN32)
|
||||
# if defined(_WIN32)
|
||||
# if defined(OPUS_BUILD) && defined(DLL_EXPORT)
|
||||
# define OPUS_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
@@ -168,15 +168,33 @@ extern "C" {
|
||||
/* Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST */
|
||||
#define OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST 4046
|
||||
#define OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST 4047
|
||||
#define OPUS_GET_IN_DTX_REQUEST 4049
|
||||
#define OPUS_SET_DRED_DURATION_REQUEST 4050
|
||||
#define OPUS_GET_DRED_DURATION_REQUEST 4051
|
||||
#define OPUS_SET_DNN_BLOB_REQUEST 4052
|
||||
/*#define OPUS_GET_DNN_BLOB_REQUEST 4053 */
|
||||
|
||||
/** Defines for the presence of extended APIs. */
|
||||
#define OPUS_HAVE_OPUS_PROJECTION_H
|
||||
|
||||
/* Macros to trigger compilation errors when the wrong types are provided to a CTL */
|
||||
#define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x))
|
||||
|
||||
#ifdef DISABLE_PTR_CHECK
|
||||
/* Disable checks to prevent ubsan from complaining about NULL checks
|
||||
in test_opus_api. */
|
||||
#define __opus_check_int_ptr(ptr) (ptr)
|
||||
#define __opus_check_uint_ptr(ptr) (ptr)
|
||||
#define __opus_check_uint8_ptr(ptr) (ptr)
|
||||
#define __opus_check_val16_ptr(ptr) (ptr)
|
||||
#define __opus_check_void_ptr(ptr) (ptr)
|
||||
#else
|
||||
#define __opus_check_int_ptr(ptr) ((ptr) + ((ptr) - (opus_int32*)(ptr)))
|
||||
#define __opus_check_uint_ptr(ptr) ((ptr) + ((ptr) - (opus_uint32*)(ptr)))
|
||||
#define __opus_check_uint8_ptr(ptr) ((ptr) + ((ptr) - (opus_uint8*)(ptr)))
|
||||
#define __opus_check_val16_ptr(ptr) ((ptr) + ((ptr) - (opus_val16*)(ptr)))
|
||||
#define __opus_check_void_ptr(x) ((void)((void *)0 == (x)), (x))
|
||||
#endif
|
||||
/** @endcond */
|
||||
|
||||
/** @defgroup opus_ctlvalues Pre-defined values for CTL interface
|
||||
@@ -481,7 +499,8 @@ extern "C" {
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Disable inband FEC (default).</dd>
|
||||
* <dt>1</dt><dd>Enable inband FEC.</dd>
|
||||
* <dt>1</dt><dd>Inband FEC enabled. If the packet loss rate is sufficiently high, Opus will automatically switch to SILK even at high rates to enable use of that FEC.</dd>
|
||||
* <dt>2</dt><dd>Inband FEC enabled, but does not necessarily switch to SILK if we have music.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_INBAND_FEC(x) OPUS_SET_INBAND_FEC_REQUEST, __opus_check_int(x)
|
||||
@@ -490,7 +509,8 @@ extern "C" {
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Inband FEC disabled (default).</dd>
|
||||
* <dt>1</dt><dd>Inband FEC enabled.</dd>
|
||||
* <dt>1</dt><dd>Inband FEC enabled. If the packet loss rate is sufficiently high, Opus will automatically switch to SILK even at high rates to enable use of that FEC.</dd>
|
||||
* <dt>2</dt><dd>Inband FEC enabled, but does not necessarily switch to SILK if we have music.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_INBAND_FEC(x) OPUS_GET_INBAND_FEC_REQUEST, __opus_check_int_ptr(x)
|
||||
@@ -617,6 +637,18 @@ extern "C" {
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_PREDICTION_DISABLED(x) OPUS_GET_PREDICTION_DISABLED_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** If non-zero, enables Deep Redundancy (DRED) and use the specified maximum number of 10-ms redundant frames
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_DRED_DURATION(x) OPUS_SET_DRED_DURATION_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured Deep Redundancy (DRED) maximum number of frames.
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_DRED_DURATION(x) OPUS_GET_DRED_DURATION_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Provide external DNN weights from binary object (only when explicitly built without the weights)
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_DNN_BLOB(data, len) OPUS_SET_DNN_BLOB_REQUEST, __opus_check_void_ptr(data), __opus_check_int(len)
|
||||
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @defgroup opus_genericctls Generic CTLs
|
||||
@@ -715,6 +747,16 @@ extern "C" {
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_PHASE_INVERSION_DISABLED(x) OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST, __opus_check_int_ptr(x)
|
||||
/** Gets the DTX state of the encoder.
|
||||
* Returns whether the last encoded frame was either a comfort noise update
|
||||
* during DTX or not encoded because of DTX.
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>The encoder is not in DTX.</dd>
|
||||
* <dt>1</dt><dd>The encoder is in DTX.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_IN_DTX(x) OPUS_GET_IN_DTX_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ extern "C" {
|
||||
* <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810004.3.9">Vorbis
|
||||
* channel ordering</a>. A decoder may wish to apply an additional permutation
|
||||
* to the mapping the encoder used to achieve a different output channel
|
||||
* order (e.g. for outputing in WAV order).
|
||||
* order (e.g. for outputting in WAV order).
|
||||
*
|
||||
* Each multistream packet contains an Opus packet for each stream, and all of
|
||||
* the Opus packets in a single multistream packet must have the same
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Submodule app/src/main/jni/moonlight-core/moonlight-common-c updated: 8af4562af6...5fa7e82c30
@@ -209,6 +209,8 @@
|
||||
<string name="summary_checkbox_vibrate_osc">Vibrates your device to emulate rumble for the on-screen controls</string>
|
||||
<string name="title_only_l3r3">Only show L3 and R3</string>
|
||||
<string name="summary_only_l3r3">Hide all virtual buttons except L3 and R3</string>
|
||||
<string name="title_show_guide_button">Show Guide Button</string>
|
||||
<string name="summary_show_guide_button">Show the guide button on screen</string>
|
||||
<string name="title_reset_osc">Clear saved on-screen controls layout</string>
|
||||
<string name="summary_reset_osc">Resets all on-screen controls to their default size and position</string>
|
||||
<string name="dialog_title_reset_osc">Reset Layout</string>
|
||||
|
||||
@@ -164,6 +164,14 @@
|
||||
android:key="checkbox_only_show_L3R3"
|
||||
android:summary="@string/summary_only_l3r3"
|
||||
android:title="@string/title_only_l3r3" />
|
||||
<CheckBoxPreference
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:defaultValue="true"
|
||||
android:dependency="checkbox_show_onscreen_controls"
|
||||
android:key="checkbox_show_guide_button"
|
||||
android:summary="@string/summary_show_guide_button"
|
||||
android:title="@string/title_show_guide_button" />
|
||||
<com.limelight.preferences.SeekBarPreference
|
||||
android:key="seekbar_osc_opacity"
|
||||
android:dependency="checkbox_show_onscreen_controls"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.2.2'
|
||||
classpath 'com.android.tools.build:gradle:8.5.1'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,748 @@
|
||||
{
|
||||
"!comment": "This is a nixpkgs Gradle dependency lockfile. For more details, refer to the Gradle section in the nixpkgs manual.",
|
||||
"!version": 1,
|
||||
"https://dl.google.com/dl/android/maven2": {
|
||||
"androidx/databinding#databinding-common/8.5.1": {
|
||||
"jar": "sha256-Zsq4JjnawPbCQzRkwJOwdNYIxLuIfsOKm4vErJgSZzI=",
|
||||
"pom": "sha256-4k+S6bAybR+VBRy5+umKG/zS4WGN+QbUI+QDLdiV5VE="
|
||||
},
|
||||
"androidx/databinding#databinding-compiler-common/8.5.1": {
|
||||
"jar": "sha256-K9foi27WtyImLkCZk1s2u6HfexzfL8CpWBj92Sq70ZI=",
|
||||
"pom": "sha256-KfpSbWLCUOe08jPN9FVmXEsGZeIBwklRewoZp1Tc/M4="
|
||||
},
|
||||
"com/android#signflinger/8.5.1": {
|
||||
"jar": "sha256-wdyixoNjTuGilCmPnHF5V4r2qG4IC9xA+WGRW8XIFC8=",
|
||||
"pom": "sha256-mMXUJdbmwXv9pgH4Va/7F0gxd4eetbzpOHFfa/jn3X8="
|
||||
},
|
||||
"com/android#zipflinger/8.5.1": {
|
||||
"jar": "sha256-gd1IVhilCaMjWSm56xMJHYhEUmYd5s5aRcw4scVVQhw=",
|
||||
"pom": "sha256-H61GAd4ulSsxCkf1b1pRNuzb2zaNvsXzU6nQ9toXLDU="
|
||||
},
|
||||
"com/android/databinding#baseLibrary/8.5.1": {
|
||||
"jar": "sha256-eUETcJ2rIbBsJis3lec8twj7rK5hcV80Nh4a9iN6GHA=",
|
||||
"pom": "sha256-VOPuTKKjm4W4yNy6+AgBKFNXZX/YQN3QiOK1lwa6Cwo="
|
||||
},
|
||||
"com/android/tools#annotations/31.5.1": {
|
||||
"jar": "sha256-slmV+nsiDTX7uOMl3wcfgpFpG/uv+XNMmOOPRewqc+4=",
|
||||
"pom": "sha256-/C0bM+cvGl2okQQ4Iv1CnuQCkW8wUooGbVhNNMb6KBY="
|
||||
},
|
||||
"com/android/tools#common/31.5.1": {
|
||||
"jar": "sha256-BkyysO4GTXFE0yeaijiytH+oNOGp+wiq0rHSFXeYgLk=",
|
||||
"pom": "sha256-zJEPF3xQbSxjWRwbRbqPMwrP3R2YBDjI98lNC9DSuAI="
|
||||
},
|
||||
"com/android/tools#dvlib/31.5.1": {
|
||||
"jar": "sha256-XMxJAlggKFBjDFnuy50ppshJuxfxD0ypAX22SfcuSB8=",
|
||||
"pom": "sha256-e0jh13cxmf7BVC+JSn36NJ3uAadhKYROBaDiJmotoyY="
|
||||
},
|
||||
"com/android/tools#repository/31.5.1": {
|
||||
"jar": "sha256-3yUz4VS3NqCpXkgEiTqlkJlVKEK/ljXNo3Ub6eiUoOI=",
|
||||
"pom": "sha256-DVjUgWMnrGOvA0mmRFYDBmCCbaVldQ9EOhIuS5N8EZY="
|
||||
},
|
||||
"com/android/tools#sdk-common/31.5.1": {
|
||||
"jar": "sha256-ymaZTQkxZ+OJshMkf0FOb5qUWE23jC5TyUuE+2C2C5o=",
|
||||
"pom": "sha256-zN6jDCw0rHrX5J2wjkGlR43paf5vHTwyDGD5f/176/E="
|
||||
},
|
||||
"com/android/tools#sdklib/31.5.1": {
|
||||
"jar": "sha256-qHx9labZwynd3oKK8/ITJTFAE1xgoL8CWpEHs8ISPyA=",
|
||||
"pom": "sha256-gh7xvOfo74t462D+20X7BSGtoGSbBqoF4f24cMO+3Ww="
|
||||
},
|
||||
"com/android/tools/analytics-library#crash/31.5.1": {
|
||||
"jar": "sha256-LFwmQglnZ1wHS8cUCDH+O/9burx/u8CLop/cPq3MlRA=",
|
||||
"pom": "sha256-OXJrDyPv30HRxITT6LBZ+HVjy1ugDDFIJ9HhIB0Kko8="
|
||||
},
|
||||
"com/android/tools/analytics-library#protos/31.5.1": {
|
||||
"jar": "sha256-F7mgPMS5aU6JAVRQIJ4T0MlYIZHdDqG48pfTbWA3ReY=",
|
||||
"pom": "sha256-mIdZOvRT0we6namMYk4EnfGXn1DrlZNa0siAPbzNgWE="
|
||||
},
|
||||
"com/android/tools/analytics-library#shared/31.5.1": {
|
||||
"jar": "sha256-Mk/EmDsURpqN8Z+lwswb09H9cpVUgl85EtjfrYuHNNM=",
|
||||
"pom": "sha256-BiteE6o/oW+2jomPwh+0rDXWLDjAFdTopiuTItbSboY="
|
||||
},
|
||||
"com/android/tools/analytics-library#tracker/31.5.1": {
|
||||
"jar": "sha256-VCxsAHCn32JUBQUy8qFKqdtVU6D8LLID/bp7chUj5xY=",
|
||||
"pom": "sha256-++t2FU5ccPtD+xyuN8GpRa03y3n+/bxIktbtfijpDDM="
|
||||
},
|
||||
"com/android/tools/build#aapt2-proto/8.5.1-11315950": {
|
||||
"jar": "sha256-LpmLPbtVLPSJump32hatI1R5Qf615WnZ6cPD9xCD58o=",
|
||||
"module": "sha256-l0TymXwXyKW1KmBsOWK5chqepCZrqbWofrQfI3XsTUg=",
|
||||
"pom": "sha256-Ss21jf5fpI7XEM9pcd0LusyJP6doMA2WWwfmmbbnIiU="
|
||||
},
|
||||
"com/android/tools/build#aaptcompiler/8.5.1": {
|
||||
"jar": "sha256-Zwo5AhZc8gM7kOZKTb7YSDgJosrn1b7crOODCWDBdv0=",
|
||||
"module": "sha256-bWaORgSw/1hwnkmwFezs+hLzxSBMrz5W8U6ztvVoJy4=",
|
||||
"pom": "sha256-HS0jQNZ+2XLBSPKPqKCUpIdvrX7AjAN4mV6o3Uz//l8="
|
||||
},
|
||||
"com/android/tools/build#apksig/8.5.1": {
|
||||
"jar": "sha256-xPb98hSEkClvQip+s3ZH6hHRw7AvHyNJwzziV/PCmx8=",
|
||||
"pom": "sha256-N5IIT8RbraUNZxfhSQSNRA504lsAbAD2tP2ye9FN/H8="
|
||||
},
|
||||
"com/android/tools/build#apkzlib/8.5.1": {
|
||||
"jar": "sha256-HBpn1vTxhkJ6wWbrqg3YZ/WV1RRPySUlKwX/udGhVrc=",
|
||||
"pom": "sha256-a3rJuq3ifLcm8yc45sVeoMK63zpg0wIIo91Rwt4t0Eo="
|
||||
},
|
||||
"com/android/tools/build#builder-model/8.5.1": {
|
||||
"jar": "sha256-ubfCzQtmkzIHr0jbd7NiRHYe0hVVxoOEnh5t21zivyc=",
|
||||
"module": "sha256-QkbxfMXzC+azlxt8q9kIq9Sf7m7XveqB5lxleqa74mg=",
|
||||
"pom": "sha256-TpxCn+I7+4NkICj92NZeaxC4hRDvxG67McxfdnlXBr4="
|
||||
},
|
||||
"com/android/tools/build#builder-test-api/8.5.1": {
|
||||
"jar": "sha256-xfxT2D6tH0ZHsUw+u2WlRMd3UxYEzx2ARj+Y4YYHNgQ=",
|
||||
"module": "sha256-PJkpNmfVudrEkqTC7n8/v4/g66ByAiSppfIiZIlAK2A=",
|
||||
"pom": "sha256-C1ChK0uyXlF8Te2OHso7J1gnX2sheHDymdqHBg7MZlQ="
|
||||
},
|
||||
"com/android/tools/build#builder/8.5.1": {
|
||||
"jar": "sha256-VeG9MUtrQR8lfryVUZEttov+ATcUcZP4SjLHiQ/x18I=",
|
||||
"module": "sha256-tbou5mIZz6Q1vM7Bp8Jiz2XlmGZi6bL8v5/wQAArGqs=",
|
||||
"pom": "sha256-zJQrbWRVtbehC+3HGYnYXpYA+3LkA1bUAM3jntnDHzc="
|
||||
},
|
||||
"com/android/tools/build#bundletool/1.16.0": {
|
||||
"jar": "sha256-HqK/UnS7rHo7tWGFIdL6EfsE6QDjOopkYCnsYzL8CMg=",
|
||||
"pom": "sha256-8uiq1EVaQjckYtXhiiaXPWdXEXmmdX84JfIn6Rry8ts="
|
||||
},
|
||||
"com/android/tools/build#gradle-api/8.5.1": {
|
||||
"jar": "sha256-j99RbDNOCilR5EHF1KoDlkS/7vaey8uAoSAmzEMASek=",
|
||||
"module": "sha256-bjognnNhBs+OI+nR+A1e81MDhexp+jQUhqG+oOuA6dA=",
|
||||
"pom": "sha256-rGKHAU+sM7FlBGARgyXeGNCH387V8x4Jo1ck8hrGm0Q="
|
||||
},
|
||||
"com/android/tools/build#gradle-settings-api/8.5.1": {
|
||||
"jar": "sha256-giZMHLRg/72siAls9xdicjQYt5zDMaSeVhtpUN8twa8=",
|
||||
"module": "sha256-Xpr9vAKvLcxwf40N923uxZxaNsaJI+dQQQbrULoyx3w=",
|
||||
"pom": "sha256-q69K2m5DEU7ZKKUBEyCVOnUpzSAT2oLBwveh16jI7b0="
|
||||
},
|
||||
"com/android/tools/build#gradle/8.5.1": {
|
||||
"jar": "sha256-A3ovU83V0KLBzGZjLy/nojlFz6+3+cJ0/iKnFuyjp5Y=",
|
||||
"module": "sha256-/v8j32Cwtb3zRavTBE00oa69u7Bnq/1QlmeD/95a5yE=",
|
||||
"pom": "sha256-t5/zHvT310fE/wWiJi19gIm8lQQQcpUQgHd8EBcNQ3A="
|
||||
},
|
||||
"com/android/tools/build#manifest-merger/31.5.1": {
|
||||
"jar": "sha256-Wv4OMmOjfBWN1CiLzMqgkVH7tMwgcf1t5wYkKvUYhH0=",
|
||||
"module": "sha256-D52bVheFKmATMMs4NfTnWe50qlbJUxDBwo539S6yQY8=",
|
||||
"pom": "sha256-d/VgqPsXO7Jr+wAL+Qx8gaSXEEHuv5d+gekfEasQq8w="
|
||||
},
|
||||
"com/android/tools/build/jetifier#jetifier-core/1.0.0-beta10": {
|
||||
"jar": "sha256-Jqu0oTkn2QYhacUEyelP6A6a46T3tauIdasAdTapH14=",
|
||||
"module": "sha256-8JF1iaQtJ2Fj8QBAq1hC6RiD3L2x1Iv9Hx/Kpywcp7c=",
|
||||
"pom": "sha256-XJ1C5rfjXU2NAuCjIs8maTs+w2QrEHyPC+WnIdRaDG0="
|
||||
},
|
||||
"com/android/tools/build/jetifier#jetifier-processor/1.0.0-beta10": {
|
||||
"jar": "sha256-xQZ6e5KCN6EnGl6ctXEOn4C0lzKTlFvFHjpMhk6kv+0=",
|
||||
"module": "sha256-NsJVdrGZk982AXBSjMYrckbDd3bWFYFUpnzfj8LVjhM=",
|
||||
"pom": "sha256-M7F/OWmJQEpJF0dIVpvI7fTjmmKkKjXOk9ylwOS6CEI="
|
||||
},
|
||||
"com/android/tools/ddms#ddmlib/31.5.1": {
|
||||
"jar": "sha256-xXHAIk/j5+ZgcZbZXMMUL9wZ8BPo8VEPaMe0pobFOAk=",
|
||||
"pom": "sha256-HdkRpTji7AzoZ6qd8qtM2B6hvnXl8NuFIajFDMVcP/M="
|
||||
},
|
||||
"com/android/tools/layoutlib#layoutlib-api/31.5.1": {
|
||||
"jar": "sha256-knG/Cz+95l51o+nFh3AbiGXS8AnqFZ8oAbqY27yKw2A=",
|
||||
"pom": "sha256-AHisaGN+NrAyh66Ux+nJZPZrbvke2TDTqWuksCYuCkA="
|
||||
},
|
||||
"com/android/tools/lint#lint-model/31.5.1": {
|
||||
"jar": "sha256-rls4Rg86WcByWTDXedu7M3LwgDGhpB+qWdn4UAs+RkE=",
|
||||
"pom": "sha256-We2jbeV5Y5gq2GzW/IAKF/ee270hHslHBk42mpGEcgU="
|
||||
},
|
||||
"com/android/tools/lint#lint-typedef-remover/31.5.1": {
|
||||
"jar": "sha256-W09IUhXKTYbvIxn8OYtfIlHmL1RGvF/Q4AZTZI3d4xg=",
|
||||
"pom": "sha256-ZMG6kvnO0A2dNczwj9PIiWWCZUYkWHzrD/C1mCEzYqU="
|
||||
},
|
||||
"com/android/tools/utp#android-device-provider-ddmlib-proto/31.5.1": {
|
||||
"jar": "sha256-2p8/Pa4mVEyQZoVJWEdl1YVKh8Ql0s/ld80002AOoJc=",
|
||||
"pom": "sha256-4wr767lO/YKnYsH0VzVlW2xHK4q+Y2nAWb3UhiH1eaY="
|
||||
},
|
||||
"com/android/tools/utp#android-device-provider-gradle-proto/31.5.1": {
|
||||
"jar": "sha256-rSNCux1vlVY0AKMiST6hwinLk985RPEmG3OZ9xhJQEk=",
|
||||
"pom": "sha256-iYdBirXnjxs2pt5rwib7h3qjeQ+S6Xg9icB4qD5CSdA="
|
||||
},
|
||||
"com/android/tools/utp#android-test-plugin-host-additional-test-output-proto/31.5.1": {
|
||||
"jar": "sha256-OEUGlN5jKMLEy6aW+cBOzdXOaVI1X2jDoi+VQdHWVG8=",
|
||||
"pom": "sha256-rdsloP8gn7f5hRXsnI9ClJsnK9SF1OSv+7gvjbVcBhg="
|
||||
},
|
||||
"com/android/tools/utp#android-test-plugin-host-apk-installer-proto/31.5.1": {
|
||||
"jar": "sha256-ztSaykAxqixXtR05CbqBUILlY8HZoka8I+3pc3glJPc=",
|
||||
"pom": "sha256-x7iXXN3eStKB2KEJujczaNKu+8LmBWe85MG04HZfvGk="
|
||||
},
|
||||
"com/android/tools/utp#android-test-plugin-host-coverage-proto/31.5.1": {
|
||||
"jar": "sha256-77TXAUqqc1UkagfC5DeiIx+yUlQP8bzmhyyI3I2onRI=",
|
||||
"pom": "sha256-uu3ksHWND1DNWwQZvXaXP3dU5RaueQoP1OVv0ivz9R8="
|
||||
},
|
||||
"com/android/tools/utp#android-test-plugin-host-emulator-control-proto/31.5.1": {
|
||||
"jar": "sha256-rt7F7EYn2JjMzfQtgDjbIOukSVdTxT0bCzeHNEkcr18=",
|
||||
"pom": "sha256-QPiWAfztmgeT+ThJAd8n7msLLixmtJ6thfawKT3Iiqw="
|
||||
},
|
||||
"com/android/tools/utp#android-test-plugin-host-logcat-proto/31.5.1": {
|
||||
"jar": "sha256-kSkCS9jjg1O8o+sm39jjYo4FjVfW6dhFH/w18BZ1HmM=",
|
||||
"pom": "sha256-YOxd/ukXyFHuWe6q0D6bZhPfJtNbX8uxbDjU2l7aAqA="
|
||||
},
|
||||
"com/android/tools/utp#android-test-plugin-host-retention-proto/31.5.1": {
|
||||
"jar": "sha256-PbjtOO9JtpTK6kZq4i47Ns7clVezWJ0OB8DN2DKUWRw=",
|
||||
"pom": "sha256-papZnheulG+JoEQmzmclP581ym21nuan4xNlrGSNwJ4="
|
||||
},
|
||||
"com/android/tools/utp#android-test-plugin-result-listener-gradle-proto/31.5.1": {
|
||||
"jar": "sha256-y99xvKYOFMMOeyz0uQ8PCj6ME498rdh0sNnArgguAnQ=",
|
||||
"pom": "sha256-3eYSlDB2g3WmtJupWX1AROW5V6fgKHg0JzADaN2axVM="
|
||||
},
|
||||
"com/google/testing/platform#core-proto/0.0.9-alpha02": {
|
||||
"jar": "sha256-bYqJBndBUPQ6j60IymTiXGBww5vYpvwTslk/KJJC/pU=",
|
||||
"pom": "sha256-J855WUJ6L/7kjQ/rRRKKPzbMQX7YqCKvoigiyPWliyU="
|
||||
}
|
||||
},
|
||||
"https://jitpack.io/com/github": {
|
||||
"cgutman#ShieldControllerExtensions/1.0.1": {
|
||||
"module": "sha256-6C1fL60OxxyaoRiGAV/s9QXC+GNUrD0BtFmhxWACpqg=",
|
||||
"pom": "sha256-P49Bt0XIkSOgXHcLrw8OeDDy2CoteSiO/ZDWaLy0I2w="
|
||||
},
|
||||
"cgutman/ShieldControllerExtensions/1.0.1/ShieldControllerExtensions-1.0.1": {
|
||||
"aar": "sha256-9T3e7fpYTbYNZuEbpnXTIoE7YHBvmyE5dr+VmU/Lc4Q="
|
||||
}
|
||||
},
|
||||
"https://repo.maven.apache.org/maven2": {
|
||||
"com/android/tools/build#transform-api/2.0.0-deprecated-use-gradle-api": {
|
||||
"jar": "sha256-6LQVGuFnnxq+ehTuNxrJs8ZRrntjKQ0fWGvdD3j6zpo=",
|
||||
"pom": "sha256-1J0Xn3B9PzoAsqfTYTa1SqjUT6IncHA82C/lL7OeIus="
|
||||
},
|
||||
"com/google/android#annotations/4.1.1.4": {
|
||||
"jar": "sha256-unNOHoTAnWFa9qCdMwNLTwRC+Hct7BIO+zdthqVlrhU=",
|
||||
"pom": "sha256-5LtUdTw2onoOXXAVSlA0/t2P6sQoIpUDS/1IPWx6rng="
|
||||
},
|
||||
"com/google/api/grpc#proto-google-common-protos/2.17.0": {
|
||||
"jar": "sha256-TvH+DDJ/wVIdHXU7CxxKh1pUvRTr3tOv/wyjlTILbqk=",
|
||||
"pom": "sha256-PwKBU6WFxZ9Viz5Dp8mAmmAai7XpEGHWxlj/+iTLjiY="
|
||||
},
|
||||
"com/google/auto#auto-parent/6": {
|
||||
"pom": "sha256-BfdAxmSBZdsAz2GN1WwgDEcl41jm1U9YU+C+wVc06go="
|
||||
},
|
||||
"com/google/auto/value#auto-value-annotations/1.6.2": {
|
||||
"jar": "sha256-tIsE3bpA6KwzvwNvBvxDmV/FCEvZS9qs6AfOJ9O+o/s=",
|
||||
"pom": "sha256-HHbNRi/JbnqpbccM6C8NVAY9bfFts1ycfZzA0amdP/8="
|
||||
},
|
||||
"com/google/auto/value#auto-value-parent/1.6.2": {
|
||||
"pom": "sha256-J7ZAyCF59c/2IAnAtyAz2bxg9g6ZAqZoAidLf+N/yBw="
|
||||
},
|
||||
"com/google/code/findbugs#jsr305/3.0.2": {
|
||||
"jar": "sha256-dmrSoHg/JoeWLIrXTO7MOKKLn3Ki0IXuQ4t4E+ko0Mc=",
|
||||
"pom": "sha256-GYidvfGyVLJgGl7mRbgUepdGRIgil2hMeYr+XWPXjf4="
|
||||
},
|
||||
"com/google/code/gson#gson-parent/2.10.1": {
|
||||
"pom": "sha256-QkjgiCQmxhUYI4XWCGw+8yYudplXGJ4pMGKAuFSCuDM="
|
||||
},
|
||||
"com/google/code/gson#gson/2.10.1": {
|
||||
"jar": "sha256-QkHBSncnw0/uplB+yAExij1KkPBw5FJWgQefuU7kxZM=",
|
||||
"pom": "sha256-0rEVY09cCF20ucn/wmWOieIx/b++IkISGhzZXU2Ujdc="
|
||||
},
|
||||
"com/google/crypto/tink#tink/1.7.0": {
|
||||
"jar": "sha256-iJcKRWoIukxmsBsj5YRsoQlcwU5Uy0g2Pl0uFaEwcwg=",
|
||||
"pom": "sha256-Ku41I3FfjyzRCyYDyNGeVhrHWDELfiyYU5RtLF57S/c="
|
||||
},
|
||||
"com/google/dagger#dagger/2.28.3": {
|
||||
"jar": "sha256-8d0j+K40qOkTZnI5kerQ1kmdGj6RY85VDCALAtdqhys=",
|
||||
"pom": "sha256-JlupWajhPDoGEz8EtTkWnBAY2v/U0z9TxFOrTLOG9XA="
|
||||
},
|
||||
"com/google/errorprone#error_prone_annotations/2.11.0": {
|
||||
"pom": "sha256-AmHKAfLS6awq4uznXULFYyOzhfspS2vJQ/Yu9Okt3wg="
|
||||
},
|
||||
"com/google/errorprone#error_prone_annotations/2.18.0": {
|
||||
"jar": "sha256-nmgUy3GBaYik/RsHqZOo8hu3BY1SLBYrHehJ4ZvqVK4=",
|
||||
"pom": "sha256-kgE1eX3MpZF7WlwBdkKljTQKTNG80S9W+JKlZjvXvdw="
|
||||
},
|
||||
"com/google/errorprone#error_prone_annotations/2.3.1": {
|
||||
"pom": "sha256-PtzmtxG6No7+Frm3qssCFPvWSEFMublllTouftiagZo="
|
||||
},
|
||||
"com/google/errorprone#error_prone_parent/2.11.0": {
|
||||
"pom": "sha256-goPwy0TGJKedMwtv2AuLinFaaLNoXJqVHD3oN9RUBVE="
|
||||
},
|
||||
"com/google/errorprone#error_prone_parent/2.18.0": {
|
||||
"pom": "sha256-R/Iumce/RmOR3vFvg3eYXl07pvW7z2WFNkSAVRPhX60="
|
||||
},
|
||||
"com/google/errorprone#error_prone_parent/2.3.1": {
|
||||
"pom": "sha256-dnUl2agRKc0IGWg4KYAzYye+QWKx4iUaGCkR2qczwSM="
|
||||
},
|
||||
"com/google/flatbuffers#flatbuffers-java/1.12.0": {
|
||||
"jar": "sha256-P4wIi03QSphYch8uFiUIyU2w3Yb5YeMG7mPvLtqHG/c=",
|
||||
"pom": "sha256-yyJrr1RiYHcPIegVKmqoi6FSMNc591DfSA8qZo1D4Os="
|
||||
},
|
||||
"com/google/guava#failureaccess/1.0.1": {
|
||||
"jar": "sha256-oXHuTHNN0tqDfksWvp30Zhr6typBra8x64Tf2vk2yiY=",
|
||||
"pom": "sha256-6WBCznj+y6DaK+lkUilHyHtAopG1/TzWcqQ0kkEDxLk="
|
||||
},
|
||||
"com/google/guava#guava-parent/26.0-android": {
|
||||
"pom": "sha256-+GmKtGypls6InBr8jKTyXrisawNNyJjUWDdCNgAWzAQ="
|
||||
},
|
||||
"com/google/guava#guava-parent/32.0.1-jre": {
|
||||
"pom": "sha256-Q+0ONrNT9B5et1zXVmZ8ni35fO8G6xYGaWcVih0DTSo="
|
||||
},
|
||||
"com/google/guava#guava/32.0.1-jre": {
|
||||
"jar": "sha256-vX+iJ1kfuFCWd9DREiz5UVjzuKn0VlP1goHYefbcSMU=",
|
||||
"pom": "sha256-QsJX9/c203ezGv7u6XirJtcwzXCvYN3nZi4YI1LiSCo="
|
||||
},
|
||||
"com/google/guava#listenablefuture/9999.0-empty-to-avoid-conflict-with-guava": {
|
||||
"jar": "sha256-s3KgN9QjCqV/vv/e8w/WEj+cDC24XQrO0AyRuXTzP5k=",
|
||||
"pom": "sha256-GNSx2yYVPU5VB5zh92ux/gXNuGLvmVSojLzE/zi4Z5s="
|
||||
},
|
||||
"com/google/j2objc#j2objc-annotations/1.3": {
|
||||
"pom": "sha256-X6yoJLoRW+5FhzAzff2y/OpGui/XdNQwTtvzD6aj8FU="
|
||||
},
|
||||
"com/google/j2objc#j2objc-annotations/2.8": {
|
||||
"jar": "sha256-8CqV+hpele2z7YWf0Pt99wnRIaNSkO/4t03OKrf01u0=",
|
||||
"pom": "sha256-N/h3mLGDhRE8kYv6nhJ2/lBzXvj6hJtYAMUZ1U2/Efg="
|
||||
},
|
||||
"com/google/jimfs#jimfs-parent/1.1": {
|
||||
"pom": "sha256-xxVVdR5X4O+RKHDorJYlrnglAqalucGcz4OyqX2LJr0="
|
||||
},
|
||||
"com/google/jimfs#jimfs/1.1": {
|
||||
"jar": "sha256-xIKOKNfAqTCvk4dRCzutp9qlwE18Jadce4sIHxwlfd0=",
|
||||
"pom": "sha256-76huXNki8XtHL9/K5XI02NSsPhSLYlBzffzkVK96ekQ="
|
||||
},
|
||||
"com/google/protobuf#protobuf-bom/3.22.3": {
|
||||
"pom": "sha256-E6Mt+53m/Bw8P3r1Pk1cd/130rR2uuOLdLdYHN7i5lU="
|
||||
},
|
||||
"com/google/protobuf#protobuf-java-util/3.22.3": {
|
||||
"jar": "sha256-xhX3aHncXDA+TfW5Smr6OVNAWMdUXbLUg/2V2fY8i/4=",
|
||||
"pom": "sha256-tEcBsGoGSGXsm1YUqT6eKPrdfU38S0YPIcgZ71Pb4tY="
|
||||
},
|
||||
"com/google/protobuf#protobuf-java/3.22.3": {
|
||||
"jar": "sha256-WdOI6motLXaujv/3/U0MYMbw9GTD06ub6OWt0JKXVwg=",
|
||||
"pom": "sha256-GG6nlBUPW0Kup+xgQd83PR2KioMWJPWKVd67YEPscxI="
|
||||
},
|
||||
"com/google/protobuf#protobuf-parent/3.22.3": {
|
||||
"pom": "sha256-OZEz1/b1eTTddsSxjoY0j0JFMhCNr0oByPgguGZfCSk="
|
||||
},
|
||||
"com/googlecode/juniversalchardet#juniversalchardet/1.0.3": {
|
||||
"jar": "sha256-dXv+kGGTuLZR553CbNZ9a1XQdwos37A4FZFQT3edSnY=",
|
||||
"pom": "sha256-eEY5mzXHzWQqmzoADD4tYtBOs3pFR7aTPMixi8wvCGs="
|
||||
},
|
||||
"com/squareup#javapoet/1.10.0": {
|
||||
"jar": "sha256-IO9LguQ/98ZSKBohMTzzuUEJJGet0/pzUJwm9pae/as=",
|
||||
"pom": "sha256-FpA0CiIiefLLrfNz6Igm+iD388w+wCUvNoGP7TJwGrE="
|
||||
},
|
||||
"com/squareup#javawriter/2.5.0": {
|
||||
"jar": "sha256-/PsJ+w6gqpfTz+fqeSOYCBNI5GjxJrNgPLOAPyQBl/A=",
|
||||
"pom": "sha256-4avX8RFs9eDFmUdpPiGJII7JQpayozlMlZ41EdOZp7A="
|
||||
},
|
||||
"com/squareup/okhttp3#okhttp/4.12.0": {
|
||||
"jar": "sha256-sQUAgbFLt6On5VpNPvAbXc+rxFO0VzpPwBl2cZHV9OA=",
|
||||
"module": "sha256-YH4iD/ghW5Kdgpu/VPMyiU8UWbTXlZea6vy8wc6lTPM=",
|
||||
"pom": "sha256-fHNwQKlBlSLnxQzAJ0FqcP58dinlKyGZNa3mtBGcfTg="
|
||||
},
|
||||
"com/squareup/okio#okio-jvm/3.6.0": {
|
||||
"jar": "sha256-Z1Q/Bzb8QirpJ+0OUEuYvF4mn9oNNQBXkzfLcT2ihBI=",
|
||||
"module": "sha256-scIZnhwMyWnvYcu+SvLsr5sGQRvd4By69vyRNN/gToo=",
|
||||
"pom": "sha256-YbTXxRWgiU/62SX9cFJiDBQlqGQz/TURO1+rDeiQpX8="
|
||||
},
|
||||
"com/squareup/okio#okio/3.6.0": {
|
||||
"module": "sha256-akesUDZOZZhFlAH7hvm2z832N7mzowRbHMM8v0xAghg=",
|
||||
"pom": "sha256-rrO3CiTBA+0MVFQfNfXFEdJ85gyuN2pZbX1lNpf4zJU="
|
||||
},
|
||||
"com/sun/activation#all/1.2.0": {
|
||||
"pom": "sha256-HYUY46x1MqEE5Pe+d97zfJguUwcjxr2z1ncIzOKwwsQ="
|
||||
},
|
||||
"com/sun/activation#all/1.2.1": {
|
||||
"pom": "sha256-NgiDv2RIbs7xYbjygvZQNTbdGmcNU6Coccj7IBcOZ5U="
|
||||
},
|
||||
"com/sun/activation#javax.activation/1.2.0": {
|
||||
"jar": "sha256-mTMCsWzXBW8h53nMV30XWoELtJAO9zzY+/K1D5KLqc4=",
|
||||
"pom": "sha256-+Hm26UWFTGkAsNvuHIOE16s95+FX/XrISTdAXEFtKl4="
|
||||
},
|
||||
"com/sun/istack#istack-commons-runtime/3.0.8": {
|
||||
"jar": "sha256-T/q7Br5FSgXkOY4gx3+itjCNS4jfvvfKMKdrW31VBe8=",
|
||||
"pom": "sha256-wuAU00y4TtKH0GSYbEXDBaQSQiinM37M9sQh0U1wjxw="
|
||||
},
|
||||
"com/sun/istack#istack-commons/3.0.8": {
|
||||
"pom": "sha256-oPBRfoUS8PvMe4KVwS9lZqPQwthtZVY53GYu+MDH6+U="
|
||||
},
|
||||
"com/sun/xml/bind#jaxb-bom-ext/2.3.2": {
|
||||
"pom": "sha256-Gn3sKyfn4FV0TNuM8bkN70/Uc6zRuATv8JgTk1iVm9c="
|
||||
},
|
||||
"com/sun/xml/bind/mvn#jaxb-parent/2.3.2": {
|
||||
"pom": "sha256-IN1tw0q3VJrEDaHYLpIiLsQ0etDsDLEY72xXA77VOhg="
|
||||
},
|
||||
"com/sun/xml/bind/mvn#jaxb-runtime-parent/2.3.2": {
|
||||
"pom": "sha256-sk+NUfGEpovBuG1IwOPP7+shpE7eHF9zA8WK4EiFM+w="
|
||||
},
|
||||
"com/sun/xml/bind/mvn#jaxb-txw-parent/2.3.2": {
|
||||
"pom": "sha256-tV0++psVj0g6MOkseMy2APkzFHM9CJ66m3RDbwGzFKQ="
|
||||
},
|
||||
"com/sun/xml/fastinfoset#FastInfoset/1.2.16": {
|
||||
"jar": "sha256-BW86HhRECfIe0Wr8JoBfWOmiHz/OFUPELUAHGdJQxRE=",
|
||||
"pom": "sha256-4UfSWKtuZpH3BZmpUkAObmx1WPjJwCjb4b4jF4MI6DA="
|
||||
},
|
||||
"com/sun/xml/fastinfoset#fastinfoset-project/1.2.16": {
|
||||
"pom": "sha256-kFgkJa3B9AtBNi2vuVFzkxIlrKpeeWINXmvVL2Rikro="
|
||||
},
|
||||
"commons-codec#commons-codec/1.10": {
|
||||
"pom": "sha256-vbjbcBLREqbj6o/bfFELMA2Z7/CBnSfd26nEM5fqTPs="
|
||||
},
|
||||
"commons-codec#commons-codec/1.11": {
|
||||
"jar": "sha256-5ZnVMY6Xqkj0ITaikn5t+k6Igd/w5sjjEJ3bv/Ude30=",
|
||||
"pom": "sha256-wecUDR3qj981KLwePFRErAtUEpcxH0X5gGwhPsPumhA="
|
||||
},
|
||||
"commons-io#commons-io/2.13.0": {
|
||||
"jar": "sha256-Zx6qOWiNrC/6pGRbPJmAri0OokceSual2hmc0VriNmY=",
|
||||
"pom": "sha256-2z/tZMLhd06/1rGnSQN3MrFJuREd1+a5hfCN2lVHBDk="
|
||||
},
|
||||
"commons-logging#commons-logging/1.2": {
|
||||
"jar": "sha256-2t3qHqC+D1aXirMAa4rJKDSv7vvZt+TmMW/KV98PpjY=",
|
||||
"pom": "sha256-yRq1qlcNhvb9B8wVjsa8LFAIBAKXLukXn+JBAHOfuyA="
|
||||
},
|
||||
"io/grpc#grpc-api/1.57.0": {
|
||||
"jar": "sha256-jSw4Qpn4Tuiqf2cPAOfLJrh+IxzzCRR0MHsyt2kQ9xw=",
|
||||
"pom": "sha256-w/BUp8iGFkfQpVglsKlJ9E/PycZPR5CD2WgTgUxQJhI="
|
||||
},
|
||||
"io/grpc#grpc-context/1.57.0": {
|
||||
"jar": "sha256-lT/KzYL1MeabduODT1gwutTCKuhBROBY1x3ICnQwJ10=",
|
||||
"pom": "sha256-qyZOgr+2q4lfYBavizzERJWryB52nDD6WprgrRa+bMY="
|
||||
},
|
||||
"io/grpc#grpc-core/1.57.0": {
|
||||
"jar": "sha256-O+5IxzvExbVb7Xm+DkhK3ya6Vr675XmN2/NHFO8eHOo=",
|
||||
"pom": "sha256-gYQEX1eR4Azyzbz16IRq/Uj1z35aTzj7W4MDx7Lv5Vs="
|
||||
},
|
||||
"io/grpc#grpc-netty/1.57.0": {
|
||||
"jar": "sha256-gdQ/LU7Rj6NBvYQKNzXxQDpwB0oEbhV+J/Z5tyG0ya0=",
|
||||
"pom": "sha256-7Z3917HtQ1avs8XRQH3ttjTIYC+0EEebSArYwROe4Xs="
|
||||
},
|
||||
"io/grpc#grpc-protobuf-lite/1.57.0": {
|
||||
"jar": "sha256-LFB8AtmBuEohdj1E4Jr08nmIHdPiW+MID2NhJYYH8Zg=",
|
||||
"pom": "sha256-sCO+cAiElIn2Uu7/df0P4aqckF9nHTROFtqv3fkhgZ0="
|
||||
},
|
||||
"io/grpc#grpc-protobuf/1.57.0": {
|
||||
"jar": "sha256-SfmG1OqxJhD9ukpokPylLV62U1mJFv24Y6Nm1eKO7Pc=",
|
||||
"pom": "sha256-wNy4xn/QHapjJW8Pi2jTcHzrfKhc2qt6PGw/9GDhPdE="
|
||||
},
|
||||
"io/grpc#grpc-stub/1.57.0": {
|
||||
"jar": "sha256-bm7hQVOfoU2fpHn39RFgVUREPH4BHnjic8+UaKoYMGA=",
|
||||
"pom": "sha256-bURZSHxiHf8xUQqIgpBjYx6RXS3Md01xkoQYEW5ZqI0="
|
||||
},
|
||||
"io/netty#netty-buffer/4.1.93.Final": {
|
||||
"jar": "sha256-AHx9nDeN8C05BWfQ1931Qv/dsCG3MT2/UCOSET/6uwg=",
|
||||
"pom": "sha256-g/vFTitzuG1Vsgj2GNGioVaRDsFG9+zldWUAe3UK3Xg="
|
||||
},
|
||||
"io/netty#netty-codec-http/4.1.93.Final": {
|
||||
"jar": "sha256-2s94znirLSlXAyXbTNJFHqWJY5gH3pWIGg+nFVqea1U=",
|
||||
"pom": "sha256-o9r/8HG20oToBj2WhD3iu4PPO4iergzJ4K22SlejG4I="
|
||||
},
|
||||
"io/netty#netty-codec-http2/4.1.93.Final": {
|
||||
"jar": "sha256-2WzAkEWhNBxtR0lDUqomO4e3L7HS6p7KFhqnOCC/6Ls=",
|
||||
"pom": "sha256-CEQztC1UH3rEtZKH3SUyhc/aOj1l3nLnNou37D02cnE="
|
||||
},
|
||||
"io/netty#netty-codec-socks/4.1.93.Final": {
|
||||
"jar": "sha256-DqR7W6I8odqOuRRsj8dVwScUFGM7Hivizh33ZLoP/yo=",
|
||||
"pom": "sha256-jNgW7ZkalGBBurTLJL2cjkHuBpJRJRHy2DzvU462Bdc="
|
||||
},
|
||||
"io/netty#netty-codec/4.1.93.Final": {
|
||||
"jar": "sha256-mQw3gWjcY2TG/1aXAfTy8SL//omYs+GJ66TE2GjtEIQ=",
|
||||
"pom": "sha256-Gc3tJnoHDf8avJ0Cm1UvrSYqzBq6XGxnsiePyhE6Jqs="
|
||||
},
|
||||
"io/netty#netty-common/4.1.93.Final": {
|
||||
"jar": "sha256-RDuzFlmfsW47rrovtYiBgU1/8LevF2/nbjgHGm6G+MA=",
|
||||
"pom": "sha256-QtiDsT6zjKv1SWFkYsXzMfUzO/DI/JIVdE+DwBgKT2s="
|
||||
},
|
||||
"io/netty#netty-handler-proxy/4.1.93.Final": {
|
||||
"jar": "sha256-KsX3+++gtz73g4iQaTRNVRVQWhSyMDvmk8UALEht8rQ=",
|
||||
"pom": "sha256-bcUNoOZ/WXgSh0+B6qRUBPfQdrgZnqkIiTKoXBthAkU="
|
||||
},
|
||||
"io/netty#netty-handler/4.1.93.Final": {
|
||||
"jar": "sha256-Tl9WOuFO1xM4GBbVgvX8/QYVrvspIDSGzft4LYoAoCs=",
|
||||
"pom": "sha256-hKFSXKwLR1nvrvKZekf+Gbm1ZC+Sc/oP1YoudsegWf4="
|
||||
},
|
||||
"io/netty#netty-parent/4.1.93.Final": {
|
||||
"pom": "sha256-sQnLdvN1/tuKnvdaxYBjFw3rfqLd0CT0Zv723GXN/O4="
|
||||
},
|
||||
"io/netty#netty-resolver/4.1.93.Final": {
|
||||
"jar": "sha256-5Zdwtm6Bgi5dERrE5UTX6wxUPgooX1JijlOUGs2O11k=",
|
||||
"pom": "sha256-WzUMPJHp5V0py+aM/k7yEWzB8DKGd+v59hW6twgsefQ="
|
||||
},
|
||||
"io/netty#netty-transport-native-unix-common/4.1.93.Final": {
|
||||
"jar": "sha256-d0FlocTbqssX+cGtZms1aaallxWugo58PUdwP0eaU+c=",
|
||||
"pom": "sha256-Fbwltn/wpJJysnDvK4z/1iAFfKFssp3/etVmGtyirhI="
|
||||
},
|
||||
"io/netty#netty-transport/4.1.93.Final": {
|
||||
"jar": "sha256-paeAGbwc1D28PHt83TgBkSyibR9Jj7VgUU/uSXhkupY=",
|
||||
"pom": "sha256-DdYqDrPLHqABpNBCbk9cCN8ccNkmVnW/+lxYNhNCLUM="
|
||||
},
|
||||
"io/perfmark#perfmark-api/0.26.0": {
|
||||
"jar": "sha256-t9I+k6NFN84zJwgmmg0UBHiKW14ZSegvVTX85Rs+qVs=",
|
||||
"module": "sha256-MdgyMyR0zkgVD1uuADNDMZE28zav0QdqKJApMZ4+qXo=",
|
||||
"pom": "sha256-ft7khhbhe2Epfq46gutIOoXlbSVnkpN4qkbzCpUDIto="
|
||||
},
|
||||
"jakarta/activation#jakarta.activation-api/1.2.1": {
|
||||
"jar": "sha256-iwoPUvqLBcVDGSGgY+2GbvqkHa3y46fuPhlh8rDZZFs=",
|
||||
"pom": "sha256-QlhcsH3afyOqBOteCUAGGUSiRqZ609FpQvvlaf8DzTE="
|
||||
},
|
||||
"jakarta/xml/bind#jakarta.xml.bind-api-parent/2.3.2": {
|
||||
"pom": "sha256-FaVbfVN8n5lwrq0o0q+XwFn2X/YQL3a70p8SR92Kbfs="
|
||||
},
|
||||
"jakarta/xml/bind#jakarta.xml.bind-api/2.3.2": {
|
||||
"jar": "sha256-aRVjBAeb3u2fwK47OTifGbPMS6REO8gFCJlTlOrXQuo=",
|
||||
"pom": "sha256-tTeziNurTMBpC50vsMdBJNZyUxc0VnrPblMTDqsTGtY="
|
||||
},
|
||||
"javax/annotation#javax.annotation-api/1.3.2": {
|
||||
"jar": "sha256-4EulGVvNVV3JVlD3zGFNFR5LzVLSmhC4qiGX86uJq5s=",
|
||||
"pom": "sha256-RqSiUcpAbnjkhT16K66DKChEpJkoUUOe6aHyNxbwa5c="
|
||||
},
|
||||
"javax/inject#javax.inject/1": {
|
||||
"jar": "sha256-kcdwRKUMSBY2wy2Rb9ickRinIZU5BFLIEGUID5V95/8=",
|
||||
"pom": "sha256-lD4SsQBieARjj6KFgFoKt4imgCZlMeZQkh6/5GIai/o="
|
||||
},
|
||||
"net/java#jvnet-parent/1": {
|
||||
"pom": "sha256-KBRAgRJo5l2eJms8yJgpfiFOBPCXQNA4bO60qJI9Y78="
|
||||
},
|
||||
"net/java#jvnet-parent/3": {
|
||||
"pom": "sha256-MPV4nvo53b+WCVqto/wSYMRWH68vcUaGcXyy3FBJR1o="
|
||||
},
|
||||
"net/java/dev/jna#jna-platform/5.6.0": {
|
||||
"jar": "sha256-ns6ovysbOZY5OdGLcEZO72DFCP7Ygg+dyroMNVGOq/c=",
|
||||
"pom": "sha256-G+s1y0GE5skGp+Murr2FLdPaCiY5YumRNKuUWDI5Tig="
|
||||
},
|
||||
"net/java/dev/jna#jna/5.6.0": {
|
||||
"jar": "sha256-VVfiNaiqL5dm1dxgnWeUjyqIMsLXls6p7x1svgs7fq8=",
|
||||
"pom": "sha256-X+gbAlWXjyRhbTexBgi3lJil8wc+HZsgONhzaoMfJgg="
|
||||
},
|
||||
"net/sf/jopt-simple#jopt-simple/4.9": {
|
||||
"jar": "sha256-JsWFbpVLX4ZNt28TuGkZtZxu7Pn9kwuWuqiIRia68vU=",
|
||||
"pom": "sha256-evfi2LJLR5jwTCt9okyfvRt1V7TgF8IFRIFWWRYHkJI="
|
||||
},
|
||||
"net/sf/kxml#kxml2/2.3.0": {
|
||||
"jar": "sha256-8mTdn3mh/eEM5ezFMiHv8kvkyTMcgwt9UvLwintjPeI=",
|
||||
"pom": "sha256-Mc5gb06VGJNimbsNJ8l4+mHhhf0d58mHT+lZpT40poU="
|
||||
},
|
||||
"org/apache#apache/13": {
|
||||
"pom": "sha256-/1E9sDYf1BI3vvR4SWi8FarkeNTsCpSW+BEHLMrzhB0="
|
||||
},
|
||||
"org/apache#apache/15": {
|
||||
"pom": "sha256-NsLy+XmsZ7RQwMtIDk6br2tA86aB8iupaSKH0ROa1JQ="
|
||||
},
|
||||
"org/apache#apache/18": {
|
||||
"pom": "sha256-eDEwcoX9R1u8NrIK4454gvEcMVOx1ZMPhS1E7ajzPBc="
|
||||
},
|
||||
"org/apache#apache/21": {
|
||||
"pom": "sha256-rxDBCNoBTxfK+se1KytLWjocGCZfoq+XoyXZFDU3s4A="
|
||||
},
|
||||
"org/apache#apache/23": {
|
||||
"pom": "sha256-vBBiTgYj82V3+sVjnKKTbTJA7RUvttjVM6tNJwVDSRw="
|
||||
},
|
||||
"org/apache#apache/29": {
|
||||
"pom": "sha256-PkkDcXSCC70N9jQgqXclWIY5iVTCoGKR+mH3J6w1s3c="
|
||||
},
|
||||
"org/apache/commons#commons-compress/1.21": {
|
||||
"jar": "sha256-auz9VFlyillWAc+gcljRMZcv/Dm0kutIvdWWV3ovJEo=",
|
||||
"pom": "sha256-Z1uwI8m+7d4yMpSZebl0Kl/qlGKApVobRi1Mp4AQiM0="
|
||||
},
|
||||
"org/apache/commons#commons-parent/34": {
|
||||
"pom": "sha256-Oi5p0G1kHR87KTEm3J4uTqZWO/jDbIfgq2+kKS0Et5w="
|
||||
},
|
||||
"org/apache/commons#commons-parent/35": {
|
||||
"pom": "sha256-cJihq4M27NTJ3CHLvKyGn4LGb2S4rE95iNQbT8tE5Jo="
|
||||
},
|
||||
"org/apache/commons#commons-parent/42": {
|
||||
"pom": "sha256-zTE0lMZwtIPsJWlyrxaYszDlmPgHACNU63ZUefYEsJw="
|
||||
},
|
||||
"org/apache/commons#commons-parent/52": {
|
||||
"pom": "sha256-ddvo806Y5MP/QtquSi+etMvNO18QR9VEYKzpBtu0UC4="
|
||||
},
|
||||
"org/apache/commons#commons-parent/58": {
|
||||
"pom": "sha256-LUsS4YiZBjq9fHUni1+pejcp2Ah4zuy2pA2UbpwNVZA="
|
||||
},
|
||||
"org/apache/httpcomponents#httpclient/4.5.14": {
|
||||
"jar": "sha256-yLx+HFGm1M5y9A0uu6vxxLaL/nbnMhBLBDgbSTR46dY=",
|
||||
"pom": "sha256-8YNVr0z4CopO8E69dCpH6Qp+rwgMclsgldvE/F2977c="
|
||||
},
|
||||
"org/apache/httpcomponents#httpcomponents-client/4.5.14": {
|
||||
"pom": "sha256-W60d5PEBRHZZ+J0ImGjMutZKaMxQPS1lQQtR9pBKoGE="
|
||||
},
|
||||
"org/apache/httpcomponents#httpcomponents-client/4.5.6": {
|
||||
"pom": "sha256-sEK0HyOR7bANNff05Qmu0hI2SMHSRs5Y0Pe5Bcn+H3M="
|
||||
},
|
||||
"org/apache/httpcomponents#httpcomponents-core/4.4.16": {
|
||||
"pom": "sha256-8tdaLC1COtGFOb8hZW1W+IpAkZRKZi/K8VnVrig9t/c="
|
||||
},
|
||||
"org/apache/httpcomponents#httpcomponents-parent/10": {
|
||||
"pom": "sha256-yq+WfZSvshdT82CCxghiBr0fSIJf9ZaTLM66crZdOfo="
|
||||
},
|
||||
"org/apache/httpcomponents#httpcomponents-parent/11": {
|
||||
"pom": "sha256-qQH4exFcVQcMfuQ+//Y+IOewLTCvJEOuKSvx9OUy06o="
|
||||
},
|
||||
"org/apache/httpcomponents#httpcore/4.4.16": {
|
||||
"jar": "sha256-bJs90UKgncRo4jrTmq1vdaDyuFElEERp8CblKkdORk8=",
|
||||
"pom": "sha256-PLrYSbNdrP5s7DGtraLGI8AmwyYRQbDSbux+OZxs1/o="
|
||||
},
|
||||
"org/apache/httpcomponents#httpmime/4.5.6": {
|
||||
"jar": "sha256-CysRAsGNPH4Fp3IUubdQGm9gVhdK5WBODiVndu2nVT4=",
|
||||
"pom": "sha256-37/W/+KnhMqYF8RjZap/ileDILgFveOdb1WgsJ2KqMo="
|
||||
},
|
||||
"org/bitbucket/b_c#jose4j/0.9.5": {
|
||||
"jar": "sha256-gI+zFm8+Z9rZgRwzECmrFoEkL9Urc1vD8z8oEWf8xy4=",
|
||||
"pom": "sha256-utAkGAobRpy9lOXy2xKEG8rFRD2VRWB/Zzz95nfB2HI="
|
||||
},
|
||||
"org/bouncycastle#bcpkix-jdk18on/1.77": {
|
||||
"jar": "sha256-Gsf+jv1bLzjNwWW+WgZ1c0/kSAjauScHIB8DpTXW8bg=",
|
||||
"pom": "sha256-j7CSbwLixLLcUuR+uwk/kvHTu28UnCpcyl4qZI0sSY0="
|
||||
},
|
||||
"org/bouncycastle#bcprov-jdk18on/1.77": {
|
||||
"jar": "sha256-2ruYwk1yybn1hWM9HfnFzVjZrTc9DNaBNn5qYDpJXVg=",
|
||||
"pom": "sha256-rROCz80DvN2L4TkTwC9E/UadCnalPPLK71vhgK3DayM="
|
||||
},
|
||||
"org/bouncycastle#bcutil-jdk18on/1.77": {
|
||||
"jar": "sha256-lHZzvLxajd4tL6aIpbdZjQym4qdKfqMM2T8E9rOtaPg=",
|
||||
"pom": "sha256-Fj36ZjL/uSinBcqDciNQys6knM1iPOc2RaXMOw+p5ug="
|
||||
},
|
||||
"org/checkerframework#checker-qual/2.5.8": {
|
||||
"pom": "sha256-M6xqDxNBrpZkfH1EZfSqPST+l9Jpe87izq5vyLXvLDw="
|
||||
},
|
||||
"org/checkerframework#checker-qual/3.33.0": {
|
||||
"jar": "sha256-4xYlW7/Nn+UNFlMUuFq7KzPLKmapPEkdtkjkmKgsLeE=",
|
||||
"module": "sha256-6FIddWJdQScsdn0mKhU6wWPMUFtmZEou9wX6iUn/tOU=",
|
||||
"pom": "sha256-9VqSICenj92LPqFaDYv+P+xqXOrDDIaqivpKW5sN9gM="
|
||||
},
|
||||
"org/codehaus/mojo#animal-sniffer-annotations/1.23": {
|
||||
"jar": "sha256-n/5Sa/Q6Y0jp2LM7nNb1gKf17tDPBVkTAH7aJj3pdNA=",
|
||||
"pom": "sha256-VhDbBrczZBrLx6DEioDEAGnbYnutBD+MfI16+09qPSc="
|
||||
},
|
||||
"org/codehaus/mojo#animal-sniffer-parent/1.23": {
|
||||
"pom": "sha256-a38FSrhqh/jiWZ81gIsJiZIuhrbKsTmIAhzRJkCktAQ="
|
||||
},
|
||||
"org/codehaus/mojo#mojo-parent/74": {
|
||||
"pom": "sha256-FHIyWhbwsb2r7SH6SDk3KWSURhApTOJoGyBZ7cZU8rM="
|
||||
},
|
||||
"org/eclipse/ee4j#project/1.0.2": {
|
||||
"pom": "sha256-dJWgenl+iOQ8O8GodCG9ix/FXjIpH6GOTjLYAx3chz8="
|
||||
},
|
||||
"org/eclipse/ee4j#project/1.0.5": {
|
||||
"pom": "sha256-kWtHlNjYIgpZo/32pk2+eUrrIzleiIuBrjaptaLFkaY="
|
||||
},
|
||||
"org/glassfish/jaxb#jaxb-bom/2.3.2": {
|
||||
"pom": "sha256-oQGLtUZ47Z9ayy96QITjhf9RAgH06dv1913GpnX2a+c="
|
||||
},
|
||||
"org/glassfish/jaxb#jaxb-runtime/2.3.2": {
|
||||
"jar": "sha256-5uCh6J+2/3hieeagCC1c71LcLr5nBT0EGABzdlK0/Rs=",
|
||||
"pom": "sha256-lEilrX+mimCD375PQsjIPggrkgKhBUAfxo6UTCZUizQ="
|
||||
},
|
||||
"org/glassfish/jaxb#txw2/2.3.2": {
|
||||
"jar": "sha256-SmqfSDOI1GG4GqmijGhbi3TAWXmTvxiEsE7dvKlfSP4=",
|
||||
"pom": "sha256-p53QAvsDgYP/KGomNb4uaMEDuH4OZHF9jUS/0Bf9M+o="
|
||||
},
|
||||
"org/jcodec#jcodec/0.2.5": {
|
||||
"jar": "sha256-iQMp2tEk6Lc5wdZgKlmlPIpHTa3f8mXCVh4hxJhJbIE=",
|
||||
"pom": "sha256-MPEyx4bSU6cgrLx3P6gApN+yPI65v3aQu7zGqjPU7oo="
|
||||
},
|
||||
"org/jdom#jdom2/2.0.6": {
|
||||
"jar": "sha256-E0XxG6YG0VYD1nQFUajCGUfAIVZAdw7GcnH+eL6pfPU=",
|
||||
"pom": "sha256-R7I6ef4za3QbgkNMbgSdaBZSVuQF51wQkh/XL6imXY0="
|
||||
},
|
||||
"org/jetbrains#annotations/13.0": {
|
||||
"jar": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=",
|
||||
"pom": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c="
|
||||
},
|
||||
"org/jetbrains#annotations/23.0.0": {
|
||||
"jar": "sha256-ew8ZckCCy/y8ZuWr6iubySzwih6hHhkZM+1DgB6zzQU=",
|
||||
"pom": "sha256-yUkPZVEyMo3yz7z990P1P8ORbWwdEENxdabKbjpndxw="
|
||||
},
|
||||
"org/jetbrains/intellij/deps#trove4j/1.0.20200330": {
|
||||
"jar": "sha256-xf1yW/+rUYRr88d9sTg8YKquv+G3/i8A0j/ht98KQ50=",
|
||||
"pom": "sha256-h3IcuqZaPJfYsbqdIHhA8WTJ/jh1n8nqEP/iZWX40+k="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-reflect/1.9.20": {
|
||||
"jar": "sha256-SbZvmonVD9KVTC6K6sgOT0iLCgkyKiXvrWJhV2cT3A8=",
|
||||
"pom": "sha256-lCtehgLTF+wTZS8cAiIFK7kIF/KM9v6dRxEvCbPo5n0="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib-common/1.9.10": {
|
||||
"jar": "sha256-zeM0G6GKK6JisLfPbFWyDJDo1DTkLJoT5qP3cNuWWog=",
|
||||
"pom": "sha256-fUtwVHkQZ2s738iSWojztr+yRYLJeEVCgFVEzu9JCpI="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib-jdk7/1.8.21": {
|
||||
"pom": "sha256-m7EH1dXjkwvFl38AekPNILfSTZGxweUo6m7g8kjxTTY="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib-jdk7/1.9.10": {
|
||||
"jar": "sha256-rGNhv5rR7TgsIQPZcSxHzewWYjK0kD7VluiHawaBybc=",
|
||||
"pom": "sha256-x/pnx5YTILidhaPKWaLhjCxlhQhFWV3K5LRq9pRe3NU="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib-jdk7/1.9.20": {
|
||||
"jar": "sha256-xUUdZ6J/M6/QmRPGfhzro4l65wiEsk7w/3EVflW2CGU=",
|
||||
"pom": "sha256-AS4cVe1q3kF7y4JBEuvqaCrWJd++4WCFw3nM+hT68DM="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib-jdk8/1.8.21": {
|
||||
"pom": "sha256-ODnXKNfDCaXDaLAnC0S08ceHj/XKXTKpogT6o0kUWdg="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib-jdk8/1.9.10": {
|
||||
"jar": "sha256-pMdNlNZM4avlN2D+A4ndlB9vxVjQ2rNeR8CFoR7IDyg=",
|
||||
"pom": "sha256-X0uU3TBlp3ZMN/oV3irW2B9A1Z+Msz8X0YHGOE+3py4="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib-jdk8/1.9.20": {
|
||||
"jar": "sha256-+DP8yU8LscMbnni9S9p+oj9Xn/NAiuGpTi61dHCGoqs=",
|
||||
"pom": "sha256-o7B96wkfKu1Z1lWYhPRPmc/135ufo1okvNa4sGnP9I0="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib/1.8.21": {
|
||||
"pom": "sha256-/gzZ4yGT5FMzP9Kx9XfmYvtavGkHECu5Z4F7wTEoD9c="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib/1.9.10": {
|
||||
"jar": "sha256-VemJxRK4CQd5n4VDCfO8d4LFs9E5MkQtA3nVxHJxFQQ=",
|
||||
"pom": "sha256-fin79z/fceBnnT3ufmgP1XNGT6AWRKT1irgZ0sCI09I="
|
||||
},
|
||||
"org/jetbrains/kotlin#kotlin-stdlib/1.9.20": {
|
||||
"jar": "sha256-KKNbzf9G2GT4DzRqYX5IYoSyCNFzeMQZAN+x3pWpDmw=",
|
||||
"module": "sha256-3Mql0xVHD6s5IFAohru4Xy2myGECxl2cBEEFRO7bIBk=",
|
||||
"pom": "sha256-43IWpzLI6Bqf0FtN2JLDDKwMrXtOP9ovlmP0jogHQcA="
|
||||
},
|
||||
"org/jmdns#jmdns/3.5.9": {
|
||||
"jar": "sha256-hoGOY4KgffYBP7sRPTy+7bE22A43cun25HhP8FiN2e8=",
|
||||
"pom": "sha256-nLQuc1ugkH8azqRHX3voQWYHs1Rpw/5jMDg1XhHh8Ao="
|
||||
},
|
||||
"org/junit#junit-bom/5.9.2": {
|
||||
"module": "sha256-qxN7pajjLJsGa/kSahx23VYUtyS6XAsCVJdyten0zx8=",
|
||||
"pom": "sha256-LtB9ZYRRMfUzaoZHbJpAVrWdC1i5gVqzZ5uw82819wU="
|
||||
},
|
||||
"org/junit#junit-bom/5.9.3": {
|
||||
"module": "sha256-tAH9JZAeWCpSSqU0PEs54ovFbiSWHBBpvytLv87ka5M=",
|
||||
"pom": "sha256-TQMpzZ5y8kIOXKFXJMv+b/puX9KIg2FRYnEZD9w0Ltc="
|
||||
},
|
||||
"org/jvnet/staxex#stax-ex/1.8.1": {
|
||||
"jar": "sha256-IFIlSQVunlCqNe8LRFouR6U9Br4LCpRn1wTiSD/7BJo=",
|
||||
"pom": "sha256-j8hPNs5tps6MiTtlOBmaf2mmmgcG2bF6PuajoJRS7tY="
|
||||
},
|
||||
"org/ow2#ow2/1.5.1": {
|
||||
"pom": "sha256-Mh3bt+5v5PU96mtM1tt0FU1r+kI5HB92OzYbn0hazwU="
|
||||
},
|
||||
"org/ow2/asm#asm-analysis/9.6": {
|
||||
"jar": "sha256-2Sgy18N+3AfGDiVZrGEYsx1kLjN6ZnHty3up+uaO27s=",
|
||||
"pom": "sha256-+j+ZUCHP9PQTkwbmz/7uoHU5EGRA0psZzAanpjahOFA="
|
||||
},
|
||||
"org/ow2/asm#asm-commons/9.6": {
|
||||
"jar": "sha256-eu/Q1cCQFwHGn3UT/tp2X7a+M68s56oXxXgfyHZXxRE=",
|
||||
"pom": "sha256-qYrkiVM0uvj/hr1mUWIQ29mgPxpuFeR92oKvz2tT13w="
|
||||
},
|
||||
"org/ow2/asm#asm-tree/9.6": {
|
||||
"jar": "sha256-xD7PF7U5x3fhXae1uGVTs3fi05poPeYoVWfVKDiI5+8=",
|
||||
"pom": "sha256-G8tIHX/Ba5VbtgygfIz6JCS87ni9xAW7oxx9b13C0RM="
|
||||
},
|
||||
"org/ow2/asm#asm-util/9.6": {
|
||||
"jar": "sha256-xjWnQC9Kqb9msvQjDOpiAloP4c1j6HKa3vybGZT6xMM=",
|
||||
"pom": "sha256-UsXB01dAR3nRqZtJqFv506CFAluFFstz2+93yK40AF4="
|
||||
},
|
||||
"org/ow2/asm#asm/9.6": {
|
||||
"jar": "sha256-PG+sJCTbPUqFO2afTj0dnDxVIjXhmjGWc/iHCDwjA6E=",
|
||||
"pom": "sha256-ku7iS8PIQ+SIHUbB3WUFRx7jFC+s+0ZrQoz+paVsa2A="
|
||||
},
|
||||
"org/slf4j#slf4j-api/1.7.30": {
|
||||
"jar": "sha256-zboHlk0btAoHYUhcax6ML4/Z6x0ZxTkorA1/lRAQXFc=",
|
||||
"pom": "sha256-fgdHdR6bZ+Gdy1IG8E6iLMA9JQxCJCZALq3QNRPywxQ="
|
||||
},
|
||||
"org/slf4j#slf4j-api/2.0.7": {
|
||||
"jar": "sha256-XWKYuToZBcMs2mR4gIrBTC1KR+kVNeU8Qff+64XZRvQ=",
|
||||
"pom": "sha256-LUA8zw4KAtXBqGZ7DiozyN/GA4qyh7lnHdaBwgUmeYE="
|
||||
},
|
||||
"org/slf4j#slf4j-parent/1.7.30": {
|
||||
"pom": "sha256-EWR5VuSKDFv7OsM/bafoPzQQAraFfv0zWlBbaHvjS3U="
|
||||
},
|
||||
"org/slf4j#slf4j-parent/2.0.7": {
|
||||
"pom": "sha256-wYK7Ns068ck8FgPN/v54iRV9swuotYT0pEU1/NIuRec="
|
||||
},
|
||||
"org/sonatype/oss#oss-parent/7": {
|
||||
"pom": "sha256-tR+IZ8kranIkmVV/w6H96ne9+e9XRyL+kM5DailVlFQ="
|
||||
},
|
||||
"org/sonatype/oss#oss-parent/9": {
|
||||
"pom": "sha256-+0AmX5glSCEv+C42LllzKyGH7G8NgBgohcFO8fmCgno="
|
||||
},
|
||||
"org/tensorflow#tensorflow-lite-metadata/0.1.0-rc2": {
|
||||
"jar": "sha256-LComT4QkmMNtNNKnuRNCSQ2alihiyFuqwazVTsL8ptk=",
|
||||
"pom": "sha256-mk9eVnQ2bBVskDkWYvA+18WXHWqmODLfdKJx2m/4LpY="
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+85
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"nodes": {
|
||||
"fleet": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-armer": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-bim": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-ci": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-emmett": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-howard": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-mermaid": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-mermaid-gpu": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-micron": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-projects": [
|
||||
"fleet",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780368078,
|
||||
"narHash": "sha256-tLzA5XveUF4PfuKNz3KuhmVhuME3PX5zvtFa17hhQPU=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "1626405d46ff3595b91c9e2d3ed9399f67c18b83",
|
||||
"revCount": 15,
|
||||
"type": "git",
|
||||
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1777268161,
|
||||
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"fleet": "fleet",
|
||||
"nixpkgs": [
|
||||
"fleet",
|
||||
"nixpkgs-projects"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
{
|
||||
description = "Moonlight game streaming client for Android — devShell + hermetic debug-APK build";
|
||||
|
||||
inputs = {
|
||||
fleet.url = "git+https://git.oleks.space/oleks/fleet-pins";
|
||||
nixpkgs.follows = "fleet/nixpkgs-projects";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
fleet,
|
||||
nixpkgs,
|
||||
}:
|
||||
let
|
||||
# Android builds run on Linux hosts; the SDK is x86_64/aarch64-linux only.
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
];
|
||||
forAllSystems =
|
||||
f:
|
||||
nixpkgs.lib.genAttrs systems (
|
||||
system:
|
||||
f (
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
config = {
|
||||
allowUnfree = true; # Android SDK components are unfree
|
||||
android_sdk.accept_license = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
# Pinned to match app/build.gradle exactly.
|
||||
ndkVersion = "27.0.12077973";
|
||||
buildToolsVersion = "34.0.0";
|
||||
platformVersion = "34";
|
||||
in
|
||||
{
|
||||
# Compose the Android SDK once per system, reused by devShell + package.
|
||||
legacyPackages = forAllSystems (pkgs: {
|
||||
androidComposition = pkgs.androidenv.composeAndroidPackages {
|
||||
platformVersions = [ platformVersion ];
|
||||
buildToolsVersions = [ buildToolsVersion ];
|
||||
includeNDK = true;
|
||||
ndkVersions = [ ndkVersion ];
|
||||
includeEmulator = false;
|
||||
includeSystemImages = false;
|
||||
includeSources = false;
|
||||
};
|
||||
});
|
||||
|
||||
# `nix develop` — JDK 17 + Android SDK/NDK + gradle, ready for `./gradlew`.
|
||||
devShells = forAllSystems (
|
||||
pkgs:
|
||||
let
|
||||
androidSdk = self.legacyPackages.${pkgs.system}.androidComposition.androidsdk;
|
||||
sdkRoot = "${androidSdk}/libexec/android-sdk";
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
packages = [
|
||||
pkgs.jdk17
|
||||
pkgs.gradle
|
||||
androidSdk
|
||||
# adb/fastboot — for sideloading the built APK to a device or an
|
||||
# Android TV (Chromecast with Google TV) over the network.
|
||||
pkgs.android-tools
|
||||
];
|
||||
|
||||
ANDROID_HOME = sdkRoot;
|
||||
ANDROID_SDK_ROOT = sdkRoot;
|
||||
ANDROID_NDK_ROOT = "${sdkRoot}/ndk/${ndkVersion}";
|
||||
JAVA_HOME = pkgs.jdk17.home;
|
||||
# AGP downloads aapt2 from Maven; that prebuilt won't run on NixOS.
|
||||
# Point it at the SDK's patched aapt2 instead.
|
||||
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${sdkRoot}/build-tools/${buildToolsVersion}/aapt2";
|
||||
|
||||
shellHook = ''
|
||||
echo "Android SDK: $ANDROID_HOME"
|
||||
echo "NDK: $ANDROID_NDK_ROOT"
|
||||
echo "Build the nonRoot debug APK with: ./gradlew assembleNonRootDebug"
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
# `nix build` — hermetic, offline debug APK build via gradle's mitm-cache.
|
||||
packages = forAllSystems (
|
||||
pkgs:
|
||||
let
|
||||
androidSdk = self.legacyPackages.${pkgs.system}.androidComposition.androidsdk;
|
||||
sdkRoot = "${androidSdk}/libexec/android-sdk";
|
||||
in
|
||||
{
|
||||
default = self.packages.${pkgs.system}.moonlight-android;
|
||||
|
||||
moonlight-android = pkgs.stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "moonlight-android";
|
||||
version = "12.1";
|
||||
|
||||
# The repo, including the moonlight-common-c + enet submodules that must
|
||||
# already be checked out (git submodule update --init --recursive).
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.gradle
|
||||
pkgs.jdk17
|
||||
androidSdk
|
||||
];
|
||||
|
||||
# Offline Maven dependency cache. Regenerate deps.json with:
|
||||
# nix build .#moonlight-android.mitmCache.updateScript && ./result
|
||||
mitmCache = pkgs.gradle.fetchDeps {
|
||||
inherit (finalAttrs) pname;
|
||||
pkg = finalAttrs.finalPackage;
|
||||
data = ./deps.json;
|
||||
};
|
||||
|
||||
gradleBuildTask = "assembleNonRootDebug";
|
||||
|
||||
ANDROID_HOME = sdkRoot;
|
||||
ANDROID_SDK_ROOT = sdkRoot;
|
||||
ANDROID_NDK_ROOT = "${sdkRoot}/ndk/${ndkVersion}";
|
||||
|
||||
gradleFlags = [
|
||||
"-Dorg.gradle.java.home=${pkgs.jdk17.home}"
|
||||
# Use the SDK's runnable aapt2 instead of the Maven prebuilt.
|
||||
"-Pandroid.aapt2FromMavenOverride=${sdkRoot}/build-tools/${buildToolsVersion}/aapt2"
|
||||
# Skip resolving androidTest/test classpaths during dependency capture
|
||||
# (AGP variant ambiguity on the self-referenced app project).
|
||||
"--init-script"
|
||||
"nix-deps-fixup.gradle"
|
||||
];
|
||||
|
||||
postPatch = ''
|
||||
# Tell Gradle where the SDK lives without relying on env detection.
|
||||
echo "sdk.dir=${sdkRoot}" > local.properties
|
||||
'';
|
||||
|
||||
# The debug build type auto-signs with a debug keystore that lands in
|
||||
# $HOME/.android; the build sandbox's HOME is read-only, so redirect it.
|
||||
preBuild = ''
|
||||
export ANDROID_USER_HOME="$NIX_BUILD_TOP/.android"
|
||||
export HOME="$NIX_BUILD_TOP/home"
|
||||
mkdir -p "$ANDROID_USER_HOME" "$HOME"
|
||||
'';
|
||||
|
||||
doCheck = false;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p "$out"
|
||||
find app/build/outputs/apk -name '*.apk' -exec cp -v {} "$out/" \;
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Moonlight game streaming client for Android (nonRoot debug APK)";
|
||||
homepage = "https://github.com/moonlight-stream/moonlight-android";
|
||||
license = pkgs.lib.licenses.gpl3Only;
|
||||
platforms = systems;
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# moonlight-android — task runner (https://github.com/casey/just)
|
||||
#
|
||||
# Wraps the Nix flake (devShell + hermetic nonRoot debug APK build). Two flake
|
||||
# gotchas are baked in so `just <recipe>` just works:
|
||||
# * `?submodules=1` — the build needs the moonlight-common-c + enet submodule
|
||||
# sources, which Nix only includes when the flake is fetched with submodules.
|
||||
# * `--builders ""` — force a LOCAL build. Without it the Android SDK/NDK
|
||||
# closure (multi-GB) gets copied to the remote builder (eu.nixbuild.net),
|
||||
# which is far slower than building here where the SDK is already realised.
|
||||
|
||||
pkg := ".?submodules=1#moonlight-android"
|
||||
build_flags := "--builders '' --print-build-logs"
|
||||
|
||||
# adb from the flake devShell (android-tools, pinned to the flake's nixpkgs).
|
||||
# The adb server daemon persists between invocations, so `connect`/`pair` in
|
||||
# one recipe line carries to the next.
|
||||
adb := "nix develop '.?submodules=1' --command adb"
|
||||
|
||||
# list available recipes
|
||||
default:
|
||||
@just --list
|
||||
|
||||
# enter the dev shell (JDK 17 + Android SDK 34 / NDK 27 + Gradle), then e.g. ./gradlew assembleNonRootDebug
|
||||
dev:
|
||||
nix develop '.?submodules=1'
|
||||
|
||||
# build the nonRoot debug APK -> ./result/app-nonRoot-debug.apk
|
||||
build:
|
||||
nix build '{{ pkg }}' {{ build_flags }}
|
||||
|
||||
# build and print the resulting APK path
|
||||
apk: build
|
||||
@ls -l result/app-nonRoot-debug.apk
|
||||
|
||||
# install the built debug APK onto a USB-connected device/emulator (needs USB debugging)
|
||||
install: build
|
||||
{{ adb }} install -r result/app-nonRoot-debug.apk
|
||||
|
||||
# install over WiFi to a Chromecast with Google TV (enable Developer options -> USB debugging first): just install-tv 192.168.1.x
|
||||
install-tv ip: build
|
||||
{{ adb }} connect {{ ip }}:5555
|
||||
{{ adb }} -s {{ ip }}:5555 install -r result/app-nonRoot-debug.apk
|
||||
@echo "Installed. Launch Moonlight from the TV's app list (apps row / 'See all apps')."
|
||||
|
||||
# Wireless-debugging pair (Developer options -> Wireless debugging -> Pair with code shows HOST:PAIRPORT + code): just tv-pair 192.168.88.19:37123 123456
|
||||
tv-pair host code:
|
||||
{{ adb }} pair {{ host }} {{ code }}
|
||||
|
||||
# connect after pairing, to the HOST:PORT on the main Wireless debugging screen (different port than pairing): just tv-connect 192.168.88.19:41234
|
||||
tv-connect host:
|
||||
{{ adb }} connect {{ host }}
|
||||
|
||||
# install the built APK to an explicit adb target (e.g. the wireless-debugging host:port)
|
||||
install-to host: build
|
||||
{{ adb }} -s {{ host }} install -r result/app-nonRoot-debug.apk
|
||||
|
||||
# launch Moonlight (Debug) on an Android TV target — uses the LEANBACK activity
|
||||
# directly (monkey's LAUNCHER category no-ops on TV): just tv-launch 192.168.88.19:43079
|
||||
tv-launch host:
|
||||
{{ adb }} -s {{ host }} shell am start -n com.limelight.debug/com.limelight.PcView
|
||||
|
||||
# disconnect a network adb device (e.g. the TV) when done
|
||||
tv-disconnect ip:
|
||||
{{ adb }} disconnect {{ ip }}:5555
|
||||
|
||||
# regenerate the offline Maven lockfile (deps.json) after changing dependencies
|
||||
deps:
|
||||
nix build '.?submodules=1#moonlight-android.mitmCache.updateScript'
|
||||
./result
|
||||
|
||||
# evaluate the flake without building (catches eval errors fast)
|
||||
check:
|
||||
nix flake check '.?submodules=1' --no-build
|
||||
|
||||
# remove the build result symlink
|
||||
clean:
|
||||
rm -f result
|
||||
@@ -0,0 +1,21 @@
|
||||
// Workaround for the nixpkgs Gradle mitm-cache hook.
|
||||
//
|
||||
// The hook's `nixDownloadDeps` task force-resolves every resolvable configuration
|
||||
// to record all dependencies. AGP's androidTest/test classpaths reference the app
|
||||
// project itself and cannot disambiguate its published variants, so resolving them
|
||||
// throws a "cannot choose between the following variants of project :app" error and
|
||||
// aborts dependency capture.
|
||||
//
|
||||
// The hermetic build target is `assembleNonRootDebug`, which never compiles tests,
|
||||
// so we mark every test configuration as non-resolvable. `nixDownloadDeps` filters
|
||||
// on `canBeResolved`, so these are simply skipped, and the real build does not touch
|
||||
// them either.
|
||||
gradle.projectsLoaded {
|
||||
rootProject.allprojects { proj ->
|
||||
proj.afterEvaluate {
|
||||
proj.configurations
|
||||
.matching { it.name.toLowerCase().contains("test") }
|
||||
.all { it.setCanBeResolved(false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user