mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-10-23 16:43:59 +00:00
Compare commits
31
Commits
frame-pacing
...
v5.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42f18cb4ac | ||
|
|
1bbd0054c2 | ||
|
|
acdde37a3a | ||
|
|
ad40e12167 | ||
|
|
1b3322b5ee | ||
|
|
6340ec6c6d | ||
|
|
babd92c8c0 | ||
|
|
7f1fe5f520 | ||
|
|
01458770d2 | ||
|
|
8d05f044f5 | ||
|
|
f5680b59a5 | ||
|
|
0ecf86c7ed | ||
|
|
6789e8d497 | ||
|
|
7d0160d556 | ||
|
|
f6a0990432 | ||
|
|
5d6094df97 | ||
|
|
d98d4aeda2 | ||
|
|
852dcf5a2d | ||
|
|
82e5aa122d | ||
|
|
fe237d1da3 | ||
|
|
e199fcd2d9 | ||
|
|
d7c6f63592 | ||
|
|
4b9c6b149a | ||
|
|
d1e41e41a1 | ||
|
|
96dfe25a14 | ||
|
|
f76d78607a | ||
|
|
90a1e68c68 | ||
|
|
a413185085 | ||
|
|
aa1b283570 | ||
|
|
f07c886711 | ||
|
|
e66b1ebec9 |
No files matched your search
+12
-6
@@ -4,19 +4,23 @@ import org.apache.tools.ant.taskdefs.condition.Os
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 26
|
||||||
buildToolsVersion '25.0.3'
|
buildToolsVersion '26.0.0'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 25
|
targetSdkVersion 26
|
||||||
|
|
||||||
versionName "5.0.1"
|
versionName "5.1"
|
||||||
versionCode = 123
|
versionCode = 128
|
||||||
}
|
}
|
||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
root {
|
root {
|
||||||
|
// Android O has native mouse capture, so don't show the rooted
|
||||||
|
// version to devices running O on the Play Store.
|
||||||
|
maxSdkVersion 25
|
||||||
|
|
||||||
applicationId "com.limelight.root"
|
applicationId "com.limelight.root"
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64"
|
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64"
|
||||||
@@ -52,6 +56,8 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile 'org.bouncycastle:bcprov-jdk15on:1.52'
|
compile 'org.bouncycastle:bcprov-jdk15on:1.52'
|
||||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.52'
|
compile 'org.bouncycastle:bcpkix-jdk15on:1.52'
|
||||||
compile project(':moonlight-common')
|
|
||||||
compile files('libs/jcodec-0.1.9-patched.jar')
|
compile files('libs/jcodec-0.1.9-patched.jar')
|
||||||
|
|
||||||
|
debugCompile project(path:':moonlight-common', configuration:'debug')
|
||||||
|
releaseCompile project(path:':moonlight-common', configuration:'release')
|
||||||
}
|
}
|
||||||
@@ -171,6 +171,20 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
streamView.setOnGenericMotionListener(this);
|
streamView.setOnGenericMotionListener(this);
|
||||||
streamView.setOnTouchListener(this);
|
streamView.setOnTouchListener(this);
|
||||||
|
|
||||||
|
inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
// The view must be focusable for pointer capture to work.
|
||||||
|
streamView.setFocusable(true);
|
||||||
|
streamView.setDefaultFocusHighlightEnabled(false);
|
||||||
|
streamView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onCapturedPointer(View view, MotionEvent motionEvent) {
|
||||||
|
return handleMotionEvent(motionEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Warn the user if they're on a metered connection
|
// Warn the user if they're on a metered connection
|
||||||
checkDataConnection();
|
checkDataConnection();
|
||||||
|
|
||||||
@@ -200,7 +214,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Initialize the MediaCodec helper before creating the decoder
|
// Initialize the MediaCodec helper before creating the decoder
|
||||||
MediaCodecHelper.initializeWithContext(this);
|
MediaCodecHelper.initializeWithContext(this);
|
||||||
|
|
||||||
decoderRenderer = new MediaCodecDecoderRenderer(prefConfig.videoFormat);
|
decoderRenderer = new MediaCodecDecoderRenderer(prefConfig.videoFormat, prefConfig.bitrate);
|
||||||
|
|
||||||
// Display a message to the user if H.265 was forced on but we still didn't find a decoder
|
// Display a message to the user if H.265 was forced on but we still didn't find a decoder
|
||||||
if (prefConfig.videoFormat == PreferenceConfiguration.FORCE_H265_ON && !decoderRenderer.isHevcSupported()) {
|
if (prefConfig.videoFormat == PreferenceConfiguration.FORCE_H265_ON && !decoderRenderer.isHevcSupported()) {
|
||||||
@@ -257,8 +271,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
getWindow().setSustainedPerformanceMode(true);
|
getWindow().setSustainedPerformanceMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this);
|
|
||||||
|
|
||||||
if (prefConfig.onscreenController) {
|
if (prefConfig.onscreenController) {
|
||||||
// create virtual onscreen controller
|
// create virtual onscreen controller
|
||||||
virtualController = new VirtualController(conn,
|
virtualController = new VirtualController(conn,
|
||||||
@@ -277,6 +289,20 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
streamView.getHolder().addCallback(this);
|
streamView.getHolder().addCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
|
super.onWindowFocusChanged(hasFocus);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
// Capture is lost when focus is lost, so it must be requested again
|
||||||
|
// when focus is regained.
|
||||||
|
if (inputCaptureProvider.isCapturingEnabled() && hasFocus) {
|
||||||
|
// Recapture the pointer if focus was regained
|
||||||
|
streamView.requestPointerCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void prepareDisplayForRendering() {
|
private void prepareDisplayForRendering() {
|
||||||
Display display = getWindowManager().getDefaultDisplay();
|
Display display = getWindowManager().getDefaultDisplay();
|
||||||
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
||||||
@@ -420,6 +446,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
unbindService(usbDriverServiceConnection);
|
unbindService(usbDriverServiceConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy the capture provider
|
||||||
|
inputCaptureProvider.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
SpinnerDialog.closeDialogs(this);
|
||||||
|
Dialog.closeDialogs();
|
||||||
|
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
int videoFormat = decoderRenderer.getActiveVideoFormat();
|
int videoFormat = decoderRenderer.getActiveVideoFormat();
|
||||||
|
|
||||||
@@ -453,14 +490,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
|
|
||||||
SpinnerDialog.closeDialogs(this);
|
|
||||||
Dialog.closeDialogs();
|
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
@@ -671,7 +700,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
|
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 ||
|
||||||
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE)
|
||||||
{
|
{
|
||||||
// This case is for mice
|
// This case is for mice
|
||||||
if (event.getSource() == InputDevice.SOURCE_MOUSE ||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||||
@@ -680,6 +710,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
{
|
{
|
||||||
int changedButtons = event.getButtonState() ^ lastButtonState;
|
int changedButtons = event.getButtonState() ^ lastButtonState;
|
||||||
|
|
||||||
|
// Ignore mouse input if we're not capturing from our input source
|
||||||
|
if (!inputCaptureProvider.isCapturingActive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
||||||
// Send the vertical scroll packet
|
// Send the vertical scroll packet
|
||||||
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
||||||
@@ -893,12 +928,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
connecting = connected = false;
|
connecting = connected = false;
|
||||||
conn.stop();
|
conn.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable cursor visibility again
|
|
||||||
inputCaptureProvider.disableCapture();
|
|
||||||
|
|
||||||
// Destroy the capture provider
|
|
||||||
inputCaptureProvider.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -908,11 +937,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
spinner = null;
|
spinner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable cursor visibility again
|
||||||
|
inputCaptureProvider.disableCapture();
|
||||||
|
|
||||||
if (!displayedFailureDialog) {
|
if (!displayedFailureDialog) {
|
||||||
displayedFailureDialog = true;
|
displayedFailureDialog = true;
|
||||||
LimeLog.severe(stage+" failed: "+errorCode);
|
LimeLog.severe(stage+" failed: "+errorCode);
|
||||||
|
|
||||||
stopConnection();
|
|
||||||
Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title),
|
Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title),
|
||||||
getResources().getString(R.string.conn_error_msg)+" "+stage, true);
|
getResources().getString(R.string.conn_error_msg)+" "+stage, true);
|
||||||
}
|
}
|
||||||
@@ -920,11 +951,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectionTerminated(long errorCode) {
|
public void connectionTerminated(long errorCode) {
|
||||||
|
// Enable cursor visibility again
|
||||||
|
inputCaptureProvider.disableCapture();
|
||||||
|
|
||||||
if (!displayedFailureDialog) {
|
if (!displayedFailureDialog) {
|
||||||
displayedFailureDialog = true;
|
displayedFailureDialog = true;
|
||||||
LimeLog.severe("Connection terminated: "+errorCode);
|
LimeLog.severe("Connection terminated: "+errorCode);
|
||||||
|
|
||||||
stopConnection();
|
stopConnection();
|
||||||
|
|
||||||
Dialog.displayDialog(this, getResources().getString(R.string.conn_terminated_title),
|
Dialog.displayDialog(this, getResources().getString(R.string.conn_terminated_title),
|
||||||
getResources().getString(R.string.conn_terminated_msg), true);
|
getResources().getString(R.string.conn_terminated_msg), true);
|
||||||
}
|
}
|
||||||
@@ -991,15 +1025,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
if (connected) {
|
// Let the decoder know immediately that the surface is gone
|
||||||
// HACK: Android is supposed to let you return from this function
|
decoderRenderer.prepareForStop();
|
||||||
// before throwing a fit if you access the surface again. Unfortunately,
|
|
||||||
// MediaCodec often tries to access the destroyed surface and triggers
|
|
||||||
// an IllegalStateException. To workaround this, we will invoke
|
|
||||||
// the DecoderRenderer's stop function ourselves, so it will hopefully
|
|
||||||
// happen early enough to not trigger the bug
|
|
||||||
decoderRenderer.stop();
|
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
stopConnection();
|
stopConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
int[] ids = InputDevice.getDeviceIds();
|
int[] ids = InputDevice.getDeviceIds();
|
||||||
for (int id : ids) {
|
for (int id : ids) {
|
||||||
InputDevice dev = InputDevice.getDevice(id);
|
InputDevice dev = InputDevice.getDevice(id);
|
||||||
|
if (dev == null) {
|
||||||
|
// This device was removed during enumeration
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 ||
|
if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 ||
|
||||||
(dev.getSources() & InputDevice.SOURCE_GAMEPAD) != 0) {
|
(dev.getSources() & InputDevice.SOURCE_GAMEPAD) != 0) {
|
||||||
// This looks like a gamepad, but we'll check X and Y to be sure
|
// This looks like a gamepad, but we'll check X and Y to be sure
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.O)
|
||||||
|
public class AndroidNativePointerCaptureProvider extends InputCaptureProvider {
|
||||||
|
|
||||||
|
private View targetView;
|
||||||
|
|
||||||
|
public AndroidNativePointerCaptureProvider(View targetView) {
|
||||||
|
this.targetView = targetView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCaptureProviderSupported() {
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enableCapture() {
|
||||||
|
super.enableCapture();
|
||||||
|
targetView.requestPointerCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disableCapture() {
|
||||||
|
super.disableCapture();
|
||||||
|
targetView.releasePointerCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCapturingActive() {
|
||||||
|
return targetView.hasPointerCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
|
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getRelativeAxisX(MotionEvent event) {
|
||||||
|
return event.getX();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getRelativeAxisY(MotionEvent event) {
|
||||||
|
return event.getY();
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-2
@@ -10,11 +10,11 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N)
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
public class AndroidCaptureProvider extends InputCaptureProvider {
|
public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
||||||
private ViewGroup rootViewGroup;
|
private ViewGroup rootViewGroup;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public AndroidCaptureProvider(Activity activity) {
|
public AndroidPointerIconCaptureProvider(Activity activity) {
|
||||||
this.context = activity;
|
this.context = activity;
|
||||||
this.rootViewGroup = (ViewGroup) activity.getWindow().getDecorView();
|
this.rootViewGroup = (ViewGroup) activity.getWindow().getDecorView();
|
||||||
}
|
}
|
||||||
@@ -33,11 +33,13 @@ public class AndroidCaptureProvider extends InputCaptureProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void enableCapture() {
|
||||||
|
super.enableCapture();
|
||||||
setPointerIconOnAllViews(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
setPointerIconOnAllViews(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableCapture() {
|
public void disableCapture() {
|
||||||
|
super.disableCapture();
|
||||||
setPointerIconOnAllViews(null);
|
setPointerIconOnAllViews(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,15 +3,17 @@ package com.limelight.binding.input.capture;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
|
import com.limelight.R;
|
||||||
import com.limelight.binding.input.evdev.EvdevCaptureProvider;
|
import com.limelight.binding.input.evdev.EvdevCaptureProvider;
|
||||||
import com.limelight.binding.input.evdev.EvdevListener;
|
import com.limelight.binding.input.evdev.EvdevListener;
|
||||||
|
|
||||||
public class InputCaptureManager {
|
public class InputCaptureManager {
|
||||||
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
|
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
|
||||||
// Shield capture is preferred because it can capture when the cursor is over
|
if (AndroidNativePointerCaptureProvider.isCaptureProviderSupported()) {
|
||||||
// the system UI. Android N native capture can only capture over views owned
|
LimeLog.info("Using Android O+ native mouse capture");
|
||||||
// by the application.
|
return new AndroidNativePointerCaptureProvider(activity.findViewById(R.id.surfaceView));
|
||||||
if (ShieldCaptureProvider.isCaptureProviderSupported()) {
|
}
|
||||||
|
else if (ShieldCaptureProvider.isCaptureProviderSupported()) {
|
||||||
LimeLog.info("Using NVIDIA mouse capture extension");
|
LimeLog.info("Using NVIDIA mouse capture extension");
|
||||||
return new ShieldCaptureProvider(activity);
|
return new ShieldCaptureProvider(activity);
|
||||||
}
|
}
|
||||||
@@ -19,9 +21,11 @@ public class InputCaptureManager {
|
|||||||
LimeLog.info("Using Evdev mouse capture");
|
LimeLog.info("Using Evdev mouse capture");
|
||||||
return new EvdevCaptureProvider(activity, rootListener);
|
return new EvdevCaptureProvider(activity, rootListener);
|
||||||
}
|
}
|
||||||
else if (AndroidCaptureProvider.isCaptureProviderSupported()) {
|
else if (AndroidPointerIconCaptureProvider.isCaptureProviderSupported()) {
|
||||||
LimeLog.info("Using Android N+ native mouse capture");
|
// Android N's native capture can't capture over system UI elements
|
||||||
return new AndroidCaptureProvider(activity);
|
// so we want to only use it if there's no other option.
|
||||||
|
LimeLog.info("Using Android N+ pointer hiding");
|
||||||
|
return new AndroidPointerIconCaptureProvider(activity);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LimeLog.info("Mouse capture not available");
|
LimeLog.info("Mouse capture not available");
|
||||||
|
|||||||
@@ -3,10 +3,25 @@ package com.limelight.binding.input.capture;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
public abstract class InputCaptureProvider {
|
public abstract class InputCaptureProvider {
|
||||||
public abstract void enableCapture();
|
protected boolean isCapturing;
|
||||||
public abstract void disableCapture();
|
|
||||||
|
public void enableCapture() {
|
||||||
|
isCapturing = true;
|
||||||
|
}
|
||||||
|
public void disableCapture() {
|
||||||
|
isCapturing = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void destroy() {}
|
public void destroy() {}
|
||||||
|
|
||||||
|
public boolean isCapturingEnabled() {
|
||||||
|
return isCapturing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCapturingActive() {
|
||||||
|
return isCapturing;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
package com.limelight.binding.input.capture;
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
|
||||||
public class NullCaptureProvider extends InputCaptureProvider {
|
public class NullCaptureProvider extends InputCaptureProvider {}
|
||||||
@Override
|
|
||||||
public void enableCapture() {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disableCapture() {}
|
|
||||||
}
|
|
||||||
@@ -63,11 +63,13 @@ public class ShieldCaptureProvider extends InputCaptureProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void enableCapture() {
|
||||||
|
super.enableCapture();
|
||||||
setCursorVisibility(false);
|
setCursorVisibility(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableCapture() {
|
public void disableCapture() {
|
||||||
|
super.disableCapture();
|
||||||
setCursorVisibility(true);
|
setCursorVisibility(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void enableCapture() {
|
||||||
|
super.enableCapture();
|
||||||
if (!started) {
|
if (!started) {
|
||||||
// Start the handler thread if it's our first time
|
// Start the handler thread if it's our first time
|
||||||
// capturing
|
// capturing
|
||||||
@@ -247,6 +248,7 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableCapture() {
|
public void disableCapture() {
|
||||||
|
super.disableCapture();
|
||||||
// This may be called on the main thread
|
// This may be called on the main thread
|
||||||
runInNetworkSafeContextSynchronously(new Runnable() {
|
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.view.SurfaceHolder;
|
|||||||
public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||||
|
|
||||||
private static final boolean USE_FRAME_RENDER_TIME = false;
|
private static final boolean USE_FRAME_RENDER_TIME = false;
|
||||||
|
private static final boolean FRAME_RENDER_TIME_ONLY = USE_FRAME_RENDER_TIME && false;
|
||||||
|
|
||||||
// Used on versions < 5.0
|
// Used on versions < 5.0
|
||||||
private ByteBuffer[] legacyInputBuffers;
|
private ByteBuffer[] legacyInputBuffers;
|
||||||
@@ -30,6 +31,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
private MediaCodecInfo avcDecoder;
|
private MediaCodecInfo avcDecoder;
|
||||||
private MediaCodecInfo hevcDecoder;
|
private MediaCodecInfo hevcDecoder;
|
||||||
|
|
||||||
|
// Used for HEVC only
|
||||||
|
private byte[] vpsBuffer;
|
||||||
|
private byte[] spsBuffer;
|
||||||
|
|
||||||
private MediaCodec videoDecoder;
|
private MediaCodec videoDecoder;
|
||||||
private Thread rendererThread;
|
private Thread rendererThread;
|
||||||
private Thread[] spinnerThreads;
|
private Thread[] spinnerThreads;
|
||||||
@@ -40,8 +45,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
private boolean refFrameInvalidationActive;
|
private boolean refFrameInvalidationActive;
|
||||||
private int initialWidth, initialHeight;
|
private int initialWidth, initialHeight;
|
||||||
private int videoFormat;
|
private int videoFormat;
|
||||||
private Object renderTarget;
|
private SurfaceHolder renderTarget;
|
||||||
private boolean stopping;
|
private volatile boolean stopping;
|
||||||
|
|
||||||
private boolean needsBaselineSpsHack;
|
private boolean needsBaselineSpsHack;
|
||||||
private SeqParameterSet savedSps;
|
private SeqParameterSet savedSps;
|
||||||
@@ -50,6 +55,11 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
private long decoderTimeMs;
|
private long decoderTimeMs;
|
||||||
private long totalTimeMs;
|
private long totalTimeMs;
|
||||||
private int totalFrames;
|
private int totalFrames;
|
||||||
|
private int frameLossEvents;
|
||||||
|
private int framesLost;
|
||||||
|
private int lastFrameNumber;
|
||||||
|
private int refreshRate;
|
||||||
|
private int bitrate;
|
||||||
|
|
||||||
private int numSpsIn;
|
private int numSpsIn;
|
||||||
private int numPpsIn;
|
private int numPpsIn;
|
||||||
@@ -91,13 +101,15 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
return decoderInfo;
|
return decoderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRenderTarget(Object renderTarget) {
|
public void setRenderTarget(SurfaceHolder renderTarget) {
|
||||||
this.renderTarget = renderTarget;
|
this.renderTarget = renderTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaCodecDecoderRenderer(int videoFormat) {
|
public MediaCodecDecoderRenderer(int videoFormat, int bitrate) {
|
||||||
//dumpDecoders();
|
//dumpDecoders();
|
||||||
|
|
||||||
|
this.bitrate = bitrate;
|
||||||
|
|
||||||
spinnerThreads = new Thread[Runtime.getRuntime().availableProcessors()];
|
spinnerThreads = new Thread[Runtime.getRuntime().availableProcessors()];
|
||||||
|
|
||||||
avcDecoder = findAvcDecoder();
|
avcDecoder = findAvcDecoder();
|
||||||
@@ -156,6 +168,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
this.initialWidth = width;
|
this.initialWidth = width;
|
||||||
this.initialHeight = height;
|
this.initialHeight = height;
|
||||||
this.videoFormat = format;
|
this.videoFormat = format;
|
||||||
|
this.refreshRate = redrawRate;
|
||||||
|
|
||||||
String mimeType;
|
String mimeType;
|
||||||
String selectedDecoderName;
|
String selectedDecoderName;
|
||||||
@@ -233,7 +246,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0);
|
videoDecoder.configure(videoFormat, renderTarget.getSurface(), null, 0);
|
||||||
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
||||||
|
|
||||||
if (USE_FRAME_RENDER_TIME && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (USE_FRAME_RENDER_TIME && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@@ -266,12 +279,12 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDecoderException(Exception e, ByteBuffer buf, int codecFlags) {
|
private void handleDecoderException(Exception e, ByteBuffer buf, int codecFlags, boolean throwOnTransient) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
if (e instanceof CodecException) {
|
if (e instanceof CodecException) {
|
||||||
CodecException codecExc = (CodecException) e;
|
CodecException codecExc = (CodecException) e;
|
||||||
|
|
||||||
if (codecExc.isTransient()) {
|
if (codecExc.isTransient() && !throwOnTransient) {
|
||||||
// We'll let transient exceptions go
|
// We'll let transient exceptions go
|
||||||
LimeLog.warning(codecExc.getDiagnosticInfo());
|
LimeLog.warning(codecExc.getDiagnosticInfo());
|
||||||
return;
|
return;
|
||||||
@@ -281,9 +294,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only throw if this happens at the beginning of a stream
|
// Only throw if the surface is still around and we're not stopping
|
||||||
// but not if we're stopping
|
if (!stopping && renderTarget.getSurface().isValid()) {
|
||||||
if (totalFrames > 0 && totalFrames < 20 && !stopping) {
|
|
||||||
if (buf != null || codecFlags != 0) {
|
if (buf != null || codecFlags != 0) {
|
||||||
throw new RendererException(this, e, buf, codecFlags);
|
throw new RendererException(this, e, buf, codecFlags);
|
||||||
}
|
}
|
||||||
@@ -339,7 +351,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleDecoderException(e, null, 0);
|
handleDecoderException(e, null, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,11 +391,11 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
startTime = MediaCodecHelper.getMonotonicMillis();
|
startTime = MediaCodecHelper.getMonotonicMillis();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (rendererThread.isAlive() && index < 0 && !stopping) {
|
while (index < 0 && !stopping) {
|
||||||
index = videoDecoder.dequeueInputBuffer(10000);
|
index = videoDecoder.dequeueInputBuffer(10000);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleDecoderException(e, null, 0);
|
handleDecoderException(e, null, 0, true);
|
||||||
return MediaCodec.INFO_TRY_AGAIN_LATER;
|
return MediaCodec.INFO_TRY_AGAIN_LATER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,16 +418,37 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
startSpinnerThreads();
|
startSpinnerThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void prepareForStop() {
|
||||||
|
// Let the decoding code know to ignore codec exceptions now
|
||||||
|
stopping = true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
stopping = true;
|
stopping = true;
|
||||||
|
|
||||||
// Halt the rendering thread
|
// Halt the rendering thread
|
||||||
rendererThread.interrupt();
|
rendererThread.interrupt();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Invalidate pending decode buffers
|
||||||
|
videoDecoder.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the renderer thread to shut down
|
||||||
try {
|
try {
|
||||||
rendererThread.join();
|
rendererThread.join();
|
||||||
} catch (InterruptedException ignored) { }
|
} catch (InterruptedException ignored) { }
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Stop the video decoder
|
||||||
|
videoDecoder.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
// Halt the spinner threads
|
// Halt the spinner threads
|
||||||
for (Thread t : spinnerThreads) {
|
for (Thread t : spinnerThreads) {
|
||||||
t.interrupt();
|
t.interrupt();
|
||||||
@@ -433,31 +466,13 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean queueInputBuffer(int inputBufferIndex, int offset, int length, long timestampUs, int codecFlags) {
|
private boolean queueInputBuffer(int inputBufferIndex, int offset, int length, long timestampUs, int codecFlags) {
|
||||||
// Try 25 times to submit the input buffer before throwing a real exception
|
try {
|
||||||
int i;
|
videoDecoder.queueInputBuffer(inputBufferIndex,
|
||||||
Exception lastException = null;
|
offset, length,
|
||||||
|
timestampUs, codecFlags);
|
||||||
for (i = 0; i < 25; i++) {
|
|
||||||
try {
|
|
||||||
videoDecoder.queueInputBuffer(inputBufferIndex,
|
|
||||||
offset, length,
|
|
||||||
timestampUs, codecFlags);
|
|
||||||
break;
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleDecoderException(e, null, codecFlags);
|
|
||||||
lastException = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 25 && totalFrames > 0 && totalFrames < 20 && !stopping) {
|
|
||||||
throw new RendererException(this, lastException, null, codecFlags);
|
|
||||||
}
|
|
||||||
else if (i != 25) {
|
|
||||||
// Queued input buffer
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch (Exception e) {
|
||||||
else {
|
handleDecoderException(e, null, codecFlags, true);
|
||||||
// Failed to queue
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,7 +483,12 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
ByteBuffer buf;
|
ByteBuffer buf;
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
buf = videoDecoder.getInputBuffer(inputBufferIndex);
|
try {
|
||||||
|
buf = videoDecoder.getInputBuffer(inputBufferIndex);
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleDecoderException(e, null, 0, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buf = legacyInputBuffers[inputBufferIndex];
|
buf = legacyInputBuffers[inputBufferIndex];
|
||||||
@@ -499,25 +519,36 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public int submitDecodeUnit(byte[] frameData, int frameLength) {
|
public int submitDecodeUnit(byte[] frameData, int frameLength, int frameNumber, long receiveTimeMs) {
|
||||||
totalFrames++;
|
totalFrames++;
|
||||||
|
|
||||||
|
// We can receive the same "frame" multiple times if it's an IDR frame.
|
||||||
|
// In that case, each frame start NALU is submitted independently.
|
||||||
|
if (frameNumber != lastFrameNumber && frameNumber != lastFrameNumber + 1) {
|
||||||
|
framesLost += frameNumber - lastFrameNumber - 1;
|
||||||
|
frameLossEvents++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFrameNumber = frameNumber;
|
||||||
|
|
||||||
|
int inputBufferIndex;
|
||||||
|
ByteBuffer buf;
|
||||||
|
|
||||||
long timestampUs = System.nanoTime() / 1000;
|
long timestampUs = System.nanoTime() / 1000;
|
||||||
|
|
||||||
|
if (!FRAME_RENDER_TIME_ONLY) {
|
||||||
|
// Count time from first packet received to decode start
|
||||||
|
totalTimeMs += (timestampUs / 1000) - receiveTimeMs;
|
||||||
|
}
|
||||||
|
|
||||||
if (timestampUs <= lastTimestampUs) {
|
if (timestampUs <= lastTimestampUs) {
|
||||||
// We can't submit multiple buffers with the same timestamp
|
// We can't submit multiple buffers with the same timestamp
|
||||||
// so bump it up by one before queuing
|
// so bump it up by one before queuing
|
||||||
timestampUs = lastTimestampUs + 1;
|
timestampUs = lastTimestampUs + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTimestampUs = timestampUs;
|
lastTimestampUs = timestampUs;
|
||||||
|
|
||||||
int inputBufferIndex = dequeueInputBuffer();
|
|
||||||
if (inputBufferIndex < 0) {
|
|
||||||
// We're being torn down now
|
|
||||||
return MoonBridge.DR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer buf = getEmptyInputBuffer(inputBufferIndex);
|
|
||||||
|
|
||||||
int codecFlags = 0;
|
int codecFlags = 0;
|
||||||
boolean needsSpsReplay = false;
|
boolean needsSpsReplay = false;
|
||||||
|
|
||||||
@@ -619,6 +650,18 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
// Patch the SPS constraint flags
|
// Patch the SPS constraint flags
|
||||||
doProfileSpecificSpsPatching(sps);
|
doProfileSpecificSpsPatching(sps);
|
||||||
|
|
||||||
|
inputBufferIndex = dequeueInputBuffer();
|
||||||
|
if (inputBufferIndex < 0) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = getEmptyInputBuffer(inputBufferIndex);
|
||||||
|
if (buf == null) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// Write the annex B header
|
// Write the annex B header
|
||||||
buf.put(frameData, 0, 5);
|
buf.put(frameData, 0, 5);
|
||||||
|
|
||||||
@@ -641,6 +684,17 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
numPpsIn++;
|
numPpsIn++;
|
||||||
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
||||||
|
|
||||||
|
inputBufferIndex = dequeueInputBuffer();
|
||||||
|
if (inputBufferIndex < 0) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = getEmptyInputBuffer(inputBufferIndex);
|
||||||
|
if (buf == null) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (needsBaselineSpsHack) {
|
if (needsBaselineSpsHack) {
|
||||||
LimeLog.info("Saw PPS; disabling SPS hack");
|
LimeLog.info("Saw PPS; disabling SPS hack");
|
||||||
@@ -652,22 +706,66 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
else if (frameData[4] == 0x40) {
|
else if (frameData[4] == 0x40) {
|
||||||
numVpsIn++;
|
numVpsIn++;
|
||||||
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
|
||||||
|
// Batch this to submit together with SPS and PPS per AOSP docs
|
||||||
|
vpsBuffer = new byte[frameLength];
|
||||||
|
System.arraycopy(frameData, 0, vpsBuffer, 0, frameLength);
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
}
|
}
|
||||||
else if (frameData[4] == 0x42) {
|
else if (frameData[4] == 0x42) {
|
||||||
numSpsIn++;
|
numSpsIn++;
|
||||||
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
|
||||||
|
// Batch this to submit together with VPS and PPS per AOSP docs
|
||||||
|
spsBuffer = new byte[frameLength];
|
||||||
|
System.arraycopy(frameData, 0, spsBuffer, 0, frameLength);
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
}
|
}
|
||||||
else if (frameData[4] == 0x44) {
|
else if (frameData[4] == 0x44) {
|
||||||
numPpsIn++;
|
numPpsIn++;
|
||||||
|
|
||||||
|
inputBufferIndex = dequeueInputBuffer();
|
||||||
|
if (inputBufferIndex < 0) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = getEmptyInputBuffer(inputBufferIndex);
|
||||||
|
if (buf == null) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When we get the PPS, submit the VPS and SPS together with
|
||||||
|
// the PPS, as required by AOSP docs on use of HEVC and MediaCodec.
|
||||||
|
if (vpsBuffer != null) {
|
||||||
|
buf.put(vpsBuffer);
|
||||||
|
}
|
||||||
|
if (spsBuffer != null) {
|
||||||
|
buf.put(spsBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the HEVC CSD blob
|
||||||
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
inputBufferIndex = dequeueInputBuffer();
|
||||||
|
if (inputBufferIndex < 0) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = getEmptyInputBuffer(inputBufferIndex);
|
||||||
|
if (buf == null) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy data from our buffer list into the input buffer
|
// Copy data from our buffer list into the input buffer
|
||||||
buf.put(frameData, 0, frameLength);
|
buf.put(frameData, 0, frameLength);
|
||||||
|
|
||||||
if (!queueInputBuffer(inputBufferIndex,
|
if (!queueInputBuffer(inputBufferIndex,
|
||||||
0, frameLength,
|
0, buf.position(),
|
||||||
timestampUs, codecFlags)) {
|
timestampUs, codecFlags)) {
|
||||||
return MoonBridge.DR_NEED_IDR;
|
return MoonBridge.DR_NEED_IDR;
|
||||||
}
|
}
|
||||||
@@ -690,6 +788,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer inputBuffer = getEmptyInputBuffer(inputIndex);
|
ByteBuffer inputBuffer = getEmptyInputBuffer(inputIndex);
|
||||||
|
if (inputBuffer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Write the Annex B header
|
// Write the Annex B header
|
||||||
inputBuffer.put(new byte[]{0x00, 0x00, 0x00, 0x01, 0x67});
|
inputBuffer.put(new byte[]{0x00, 0x00, 0x00, 0x01, 0x67});
|
||||||
@@ -779,8 +880,11 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n";
|
str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n";
|
||||||
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
||||||
str += "Initial video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
str += "Initial video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
||||||
|
str += "FPS target: "+renderer.refreshRate+"\n";
|
||||||
|
str += "Bitrate: "+renderer.bitrate+" Mbps \n";
|
||||||
str += "In stats: "+renderer.numVpsIn+", "+renderer.numSpsIn+", "+renderer.numPpsIn+"\n";
|
str += "In stats: "+renderer.numVpsIn+", "+renderer.numSpsIn+", "+renderer.numPpsIn+"\n";
|
||||||
str += "Total frames: "+renderer.totalFrames+"\n";
|
str += "Total frames: "+renderer.totalFrames+"\n";
|
||||||
|
str += "Frame losses: "+renderer.framesLost+" in "+frameLossEvents+" loss events\n";
|
||||||
str += "Average end-to-end client latency: "+getAverageEndToEndLatency()+"ms\n";
|
str += "Average end-to-end client latency: "+getAverageEndToEndLatency()+"ms\n";
|
||||||
str += "Average hardware decoder latency: "+getAverageDecoderLatency()+"ms\n";
|
str += "Average hardware decoder latency: "+getAverageDecoderLatency()+"ms\n";
|
||||||
|
|
||||||
@@ -796,13 +900,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
|
|
||||||
str += "Is Exynos 4: "+renderer.isExynos4+"\n";
|
str += "Is Exynos 4: "+renderer.isExynos4+"\n";
|
||||||
|
|
||||||
str += "/proc/cpuinfo:\n";
|
|
||||||
try {
|
|
||||||
str += MediaCodecHelper.readCpuinfo();
|
|
||||||
} catch (Exception e) {
|
|
||||||
str += e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
str += "Full decoder dump:\n";
|
str += "Full decoder dump:\n";
|
||||||
try {
|
try {
|
||||||
str += MediaCodecHelper.dumpDecoders();
|
str += MediaCodecHelper.dumpDecoders();
|
||||||
|
|||||||
@@ -63,15 +63,24 @@ public class MediaCodecHelper {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
blacklistedDecoderPrefixes = new LinkedList<>();
|
blacklistedDecoderPrefixes = new LinkedList<>();
|
||||||
|
|
||||||
// Software decoders that don't support H264 high profile
|
// Blacklist software decoders that don't support H264 high profile,
|
||||||
blacklistedDecoderPrefixes.add("omx.google");
|
// but exclude the official AOSP emulator from this restriction.
|
||||||
blacklistedDecoderPrefixes.add("AVCDecoder");
|
if (!Build.HARDWARE.equals("ranchu") || !Build.BRAND.equals("google")) {
|
||||||
|
blacklistedDecoderPrefixes.add("omx.google");
|
||||||
|
blacklistedDecoderPrefixes.add("AVCDecoder");
|
||||||
|
}
|
||||||
|
|
||||||
// Without bitstream fixups, we perform horribly on NVIDIA's HEVC
|
// Without bitstream fixups, we perform horribly on NVIDIA's HEVC
|
||||||
// decoder. While not strictly necessary, I'm going to fully blacklist this
|
// decoder. While not strictly necessary, I'm going to fully blacklist this
|
||||||
// one to avoid users getting inaccurate impressions of Tegra X1/Moonlight performance.
|
// one to avoid users getting inaccurate impressions of Tegra X1/Moonlight performance.
|
||||||
blacklistedDecoderPrefixes.add("OMX.Nvidia.h265.decode");
|
blacklistedDecoderPrefixes.add("OMX.Nvidia.h265.decode");
|
||||||
|
|
||||||
|
// Force these decoders disabled because:
|
||||||
|
// 1) They are software decoders, so the performance is terrible
|
||||||
|
// 2) They crash with our HEVC stream anyway (at least prior to CSD batching)
|
||||||
|
blacklistedDecoderPrefixes.add("OMX.qcom.video.decoder.hevcswvdec");
|
||||||
|
blacklistedDecoderPrefixes.add("OMX.SEC.hevc.sw.dec");
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -98,6 +107,11 @@ public class MediaCodecHelper {
|
|||||||
static {
|
static {
|
||||||
whitelistedHevcDecoders = new LinkedList<>();
|
whitelistedHevcDecoders = new LinkedList<>();
|
||||||
|
|
||||||
|
// Allow software HEVC decoding in the official AOSP emulator
|
||||||
|
if (Build.HARDWARE.equals("ranchu") && Build.BRAND.equals("google")) {
|
||||||
|
whitelistedHevcDecoders.add("omx.google");
|
||||||
|
}
|
||||||
|
|
||||||
// Exynos seems to be the only HEVC decoder that works reliably
|
// Exynos seems to be the only HEVC decoder that works reliably
|
||||||
whitelistedHevcDecoders.add("omx.exynos");
|
whitelistedHevcDecoders.add("omx.exynos");
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.3.2'
|
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule moonlight-common updated: d7f9be6c27...13bf4f8855.
Reference in new issue
Block a user