Compare commits

..

18 Commits

Author SHA1 Message Date
Cameron Gutman 1f09cbd609 Fix mouse scrolling and remove unreachable code 2014-10-03 23:20:09 -07:00
Cameron Gutman b6ee0764ff Clear connection state before stopping to avoid potential deadlocks 2014-10-03 23:07:42 -07:00
Cameron Gutman f56b7ff79e Bump to 2.5.6 2014-10-03 22:56:30 -07:00
Cameron Gutman 645ea683ee Increase the minimum deadzone to 12% because I'm paranoid 2014-10-03 22:52:48 -07:00
Cameron Gutman 67e22fca6b Improve re-hiding of the system UI by using a proper system UI visibility listener 2014-10-03 22:49:36 -07:00
Cameron Gutman a726ba8ea7 Add an ungrab key combo (Ctrl+Shift+Z). Ignore repeat key down events. Fix some mishandling of input events that could cause crashes. 2014-10-03 22:18:36 -07:00
Cameron Gutman 23fcaa1bab Add axis scaling support 2014-10-01 20:24:40 -07:00
Cameron Gutman ad684a6f6b Merge version update 2014-09-28 16:38:56 -07:00
Cameron Gutman d3438f4938 Update common jar 2014-09-28 16:37:51 -07:00
Cameron Gutman cafdc21bf2 Prefer Samsung's OMX.SEC.AVC.Decoder if it's in the list of decoders 2014-09-28 16:37:41 -07:00
Cameron Gutman ceb9bd3342 Change bitstream restrictions to match default values 2014-09-28 16:37:30 -07:00
Cameron Gutman 13b80eda8a Use a common cleanup function and stop input capturing after closing the connection to allow captured devices to accept the "Connection Failed" dialog 2014-09-27 19:32:10 -07:00
Cameron Gutman 6677949614 Update common jar 2014-09-27 15:49:59 -07:00
Cameron Gutman 080dcd92d7 Suppress a warning 2014-09-27 15:45:54 -07:00
Cameron Gutman 31b0bcf041 Fix manually adding PCs 2014-09-27 15:45:37 -07:00
Cameron Gutman 36664133f8 Speed up PC polling by only trying once if the remote and local IPs are the same 2014-09-27 15:43:43 -07:00
Cameron Gutman a3106bffca Speed up initial discovery by generating a new keypair while discovering machines. 2014-09-27 15:42:12 -07:00
Cameron Gutman 94a26fb831 Force CPU decoding to low performance to make the experience less horrible 2014-09-26 20:50:34 -07:00
10 changed files with 345 additions and 83 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.limelight"
android:versionCode="34"
android:versionName="2.5.5" >
android:versionCode="35"
android:versionName="2.5.6" >
<uses-sdk
android:minSdkVersion="16"
Binary file not shown.
+218 -66
View File
@@ -35,6 +35,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnGenericMotionListener;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.Window;
@@ -43,7 +44,8 @@ import android.widget.Toast;
public class Game extends Activity implements SurfaceHolder.Callback,
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
OnSystemUiVisibilityChangeListener
{
private int lastMouseX = Integer.MIN_VALUE;
private int lastMouseY = Integer.MIN_VALUE;
@@ -69,6 +71,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private boolean toastsDisabled;
private EvdevWatcher evdevWatcher;
private int modifierFlags = 0;
private boolean grabbedInput = true;
private boolean grabComboDown = false;
private static final int MODIFIER_CTRL = 0x1;
private static final int MODIFIER_SHIFT = 0x2;
private ConfigurableDecoderRenderer decoderRenderer;
@@ -133,6 +140,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
}
// Listen for UI visibility events
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
// Change volume button behavior
setVolumeControlStream(AudioManager.STREAM_MUSIC);
@@ -270,11 +280,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
};
private void hideSystemUi() {
private void hideSystemUi(int delay) {
Handler h = getWindow().getDecorView().getHandler();
if (h != null) {
h.removeCallbacks(hideSystemUi);
h.postDelayed(hideSystemUi, 1000);
h.postDelayed(hideSystemUi, delay);
}
}
@@ -286,7 +296,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
Dialog.closeDialogs();
displayedFailureDialog = true;
conn.stop();
stopConnection();
int averageEndToEndLat = decoderRenderer.getAverageEndToEndLatency();
int averageDecoderLat = decoderRenderer.getAverageDecoderLatency();
@@ -304,10 +314,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (message != null) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
if (evdevWatcher != null) {
evdevWatcher.shutdown();
}
finish();
}
@@ -319,6 +325,80 @@ public class Game extends Activity implements SurfaceHolder.Callback,
wifiLock.release();
}
private Runnable toggleGrab = new Runnable() {
@Override
public void run() {
if (evdevWatcher != null) {
if (grabbedInput) {
evdevWatcher.ungrabAll();
}
else {
evdevWatcher.regrabAll();
}
}
grabbedInput = !grabbedInput;
}
};
// Returns true if the key stroke was consumed
private boolean handleMagicKeyCombos(short translatedKey, boolean down) {
int modifierMask = 0;
// Mask off the high byte
translatedKey &= 0xff;
if (translatedKey == KeyboardTranslator.VK_CONTROL) {
modifierMask = MODIFIER_CTRL;
}
else if (translatedKey == KeyboardTranslator.VK_SHIFT) {
modifierMask = MODIFIER_SHIFT;
}
if (down) {
this.modifierFlags |= modifierMask;
}
else {
this.modifierFlags &= ~modifierMask;
}
// Check if Ctrl+Shift+Z is pressed
if (translatedKey == KeyboardTranslator.VK_Z &&
(modifierFlags & (MODIFIER_CTRL|MODIFIER_SHIFT)) == (MODIFIER_CTRL|MODIFIER_SHIFT))
{
if (down) {
// Now that we've pressed the magic combo
// we'll wait for one of the keys to come up
grabComboDown = true;
}
else {
// Toggle the grab if Z comes up
Handler h = getWindow().getDecorView().getHandler();
if (h != null) {
h.postDelayed(toggleGrab, 250);
}
grabComboDown = false;
}
return true;
}
// Toggle the grab if control or shift comes up
else if (grabComboDown) {
Handler h = getWindow().getDecorView().getHandler();
if (h != null) {
h.postDelayed(toggleGrab, 250);
}
grabComboDown = false;
return true;
}
// Not a special combo
return false;
}
private static byte getModifierState(KeyEvent event) {
byte modifier = 0;
if (event.isShiftPressed()) {
@@ -354,6 +434,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return super.onKeyDown(keyCode, event);
}
// Let this method take duplicate key down events
if (handleMagicKeyCombos(translated, true)) {
return true;
}
// Eat repeat down events
if (event.getRepeatCount() > 0) {
return true;
}
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return super.onKeyDown(keyCode, event);
}
keybTranslator.sendKeyDown(translated,
getModifierState(event));
}
@@ -363,16 +458,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Pressing a volume button drops the immersive flag so the UI shows up again and doesn't
// go away. I'm not sure if that's a bug or a feature, but we're working around it here
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
Handler h = getWindow().getDecorView().getHandler();
if (h != null) {
h.removeCallbacks(hideSystemUi);
h.postDelayed(hideSystemUi, 2000);
}
}
InputDevice dev = event.getDevice();
if (dev == null) {
return super.onKeyUp(keyCode, event);
@@ -392,6 +477,15 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return super.onKeyUp(keyCode, event);
}
if (handleMagicKeyCombos(translated, false)) {
return true;
}
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return super.onKeyUp(keyCode, event);
}
keybTranslator.sendKeyUp(translated,
getModifierState(event));
}
@@ -408,25 +502,35 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return null;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
// Returns true if the event was consumed
private boolean handleMotionEvent(MotionEvent event) {
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return false;
}
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (controllerHandler.handleMotionEvent(event)) {
return true;
}
}
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
{
// This case is for touch-based input devices
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN ||
event.getSource() == InputDevice.SOURCE_STYLUS)
event.getSource() == InputDevice.SOURCE_STYLUS)
{
int actionIndex = event.getActionIndex();
int eventX = (int)event.getX(actionIndex);
int eventY = (int)event.getY(actionIndex);
TouchContext context = getTouchContext(actionIndex);
if (context == null) {
return super.onTouchEvent(event);
return false;
}
switch (event.getActionMasked())
{
case MotionEvent.ACTION_POINTER_DOWN:
@@ -449,7 +553,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
break;
default:
return super.onTouchEvent(event);
return false;
}
}
// This case is for mice
@@ -457,6 +561,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
{
int changedButtons = event.getButtonState() ^ lastButtonState;
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
// Send the vertical scroll packet
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
conn.sendMouseScroll(vScrollClicks);
}
if ((changedButtons & MotionEvent.BUTTON_PRIMARY) != 0) {
if ((event.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0) {
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_LEFT);
@@ -465,7 +575,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
}
}
if ((changedButtons & MotionEvent.BUTTON_SECONDARY) != 0) {
if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
@@ -474,7 +584,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
}
}
if ((changedButtons & MotionEvent.BUTTON_TERTIARY) != 0) {
if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) != 0) {
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_MIDDLE);
@@ -483,47 +593,41 @@ public class Game extends Activity implements SurfaceHolder.Callback,
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_MIDDLE);
}
}
updateMousePosition((int)event.getX(), (int)event.getY());
lastButtonState = event.getButtonState();
}
else
{
return super.onTouchEvent(event);
// Unknown source
return false;
}
// Handled a known source
return true;
}
// Unknown class
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!handleMotionEvent(event)) {
return super.onTouchEvent(event);
}
return super.onTouchEvent(event);
return true;
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (controllerHandler.handleMotionEvent(event)) {
return true;
}
}
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
{
switch (event.getActionMasked())
{
case MotionEvent.ACTION_HOVER_MOVE:
// Send a mouse move update (if neccessary)
updateMousePosition((int)event.getX(), (int)event.getY());
break;
case MotionEvent.ACTION_SCROLL:
// Send the vertical scroll packet
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
conn.sendMouseScroll(vScrollClicks);
break;
}
return true;
if (!handleMotionEvent(event)) {
return super.onGenericMotionEvent(event);
}
return super.onGenericMotionEvent(event);
return true;
}
private void updateMousePosition(int eventX, int eventY) {
@@ -551,15 +655,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
// Send it to the activity's motion event handler
return onGenericMotionEvent(event);
return handleMotionEvent(event);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
// Send it to the activity's touch event handler
return onTouchEvent(event);
return handleMotionEvent(event);
}
@Override
@@ -572,6 +674,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public void stageComplete(Stage stage) {
}
private void stopConnection() {
if (connecting || connected) {
connecting = connected = false;
conn.stop();
}
// Close the Evdev watcher to allow use of captured input devices
if (evdevWatcher != null) {
evdevWatcher.shutdown();
evdevWatcher = null;
}
}
@Override
public void stageFailed(Stage stage) {
@@ -582,9 +697,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (!displayedFailureDialog) {
displayedFailureDialog = true;
stopConnection();
Dialog.displayDialog(this, "Connection Error", "Starting "+stage.getName()+" failed", true);
conn.stop();
connecting = false;
}
}
@@ -593,9 +707,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (!displayedFailureDialog) {
displayedFailureDialog = true;
e.printStackTrace();
stopConnection();
Dialog.displayDialog(this, "Connection Terminated", "The connection failed unexpectedly", true);
conn.stop();
connected = false;
}
}
@@ -609,7 +723,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
connecting = false;
connected = true;
hideSystemUi();
hideSystemUi(1000);
}
@Override
@@ -657,8 +771,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (connected) {
conn.stop();
connected = false;
stopConnection();
}
}
@@ -699,4 +812,43 @@ public class Game extends Activity implements SurfaceHolder.Callback,
public void mouseScroll(byte amount) {
conn.sendMouseScroll(amount);
}
public void keyboardEvent(boolean buttonDown, short keyCode) {
short keyMap = keybTranslator.translate(keyCode);
if (keyMap != 0) {
if (handleMagicKeyCombos(keyMap, buttonDown)) {
return;
}
if (buttonDown) {
keybTranslator.sendKeyDown(keyMap, (byte) 0);
}
else {
keybTranslator.sendKeyUp(keyMap, (byte) 0);
}
}
}
@Override
public void onSystemUiVisibilityChange(int visibility) {
// Don't do anything if we're not connected
if (!connected) {
return;
}
// This flag is set for all devices
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) {
hideSystemUi(2000);
}
}
}
+4
View File
@@ -6,6 +6,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import com.limelight.binding.PlatformBinding;
import com.limelight.binding.crypto.AndroidCryptoProvider;
import com.limelight.computers.ComputerManagerListener;
import com.limelight.computers.ComputerManagerService;
import com.limelight.nvstream.http.ComputerDetails;
@@ -59,6 +60,9 @@ public class PcView extends Activity {
// Start updates
startComputerUpdates();
// Force a keypair to be generated early to avoid discovery delays
new AndroidCryptoProvider(PcView.this).getClientCertificate();
}
}.start();
}
@@ -51,6 +51,8 @@ public class AndroidCryptoProvider implements LimelightCryptoProvider {
private RSAPrivateKey key;
private byte[] pemCertBytes;
private static Object globalCryptoLock = new Object();
static {
// Install the Bouncy Castle provider
Security.addProvider(new BouncyCastleProvider());
@@ -208,7 +210,7 @@ public class AndroidCryptoProvider implements LimelightCryptoProvider {
public X509Certificate getClientCertificate() {
// Use a lock here to ensure only one guy will be generating or loading
// the certificate and key at a time
synchronized (this) {
synchronized (globalCryptoLock) {
// Return a loaded cert if we have one
if (cert != null) {
return cert;
@@ -235,7 +237,7 @@ public class AndroidCryptoProvider implements LimelightCryptoProvider {
public RSAPrivateKey getClientPrivateKey() {
// Use a lock here to ensure only one guy will be generating or loading
// the certificate and key at a time
synchronized (this) {
synchronized (globalCryptoLock) {
// Return a loaded key if we have one
if (key != null) {
return key;
@@ -260,7 +262,7 @@ public class AndroidCryptoProvider implements LimelightCryptoProvider {
}
public byte[] getPemEncodedClientCertificate() {
synchronized (this) {
synchronized (globalCryptoLock) {
// Call our helper function to do the cert loading/generation for us
getClientCertificate();
@@ -47,6 +47,9 @@ public class ControllerHandler {
public ControllerHandler(NvConnection conn) {
this.conn = conn;
// We want limelight-common to scale the axis values to match Xinput values
ControllerPacket.enableAxisScaling = true;
}
private ControllerMapping createMappingForDevice(InputDevice dev) {
@@ -135,6 +138,15 @@ public class ControllerHandler {
InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis);
if (lsXRange != null && lsYRange != null) {
mapping.leftStickDeadzoneRadius = Math.max(lsXRange.getFlat(), lsYRange.getFlat());
// If there isn't a (reasonable) deadzone at all, use 20%
if (mapping.leftStickDeadzoneRadius < 0.02f) {
mapping.leftStickDeadzoneRadius = 0.20f;
}
// Check that the deadzone is 12% at minimum
else if (mapping.leftStickDeadzoneRadius < 0.12f) {
mapping.leftStickDeadzoneRadius = 0.12f;
}
}
}
@@ -143,6 +155,15 @@ public class ControllerHandler {
InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis);
if (rsXRange != null && rsYRange != null) {
mapping.rightStickDeadzoneRadius = Math.max(rsXRange.getFlat(), rsYRange.getFlat());
// If there isn't a (reasonable) deadzone at all, use 20%
if (mapping.rightStickDeadzoneRadius < 0.02f) {
mapping.rightStickDeadzoneRadius = 0.20f;
}
// Check that the deadzone is 12% at minimum
else if (mapping.rightStickDeadzoneRadius < 0.12f) {
mapping.rightStickDeadzoneRadius = 0.12f;
}
}
}
@@ -242,7 +263,7 @@ public class ControllerHandler {
// Scale the input based on the distance from the deadzone
inputVector.getNormalized(normalizedInputVector);
normalizedInputVector.scalarMultiply((inputVector.getMagnitude() - deadzoneRadius) / (1.0f - deadzoneRadius));
// Bound the X value to -1.0 to 1.0
if (normalizedInputVector.getX() > 1.0f) {
normalizedInputVector.setX(1.0f);
@@ -284,7 +305,7 @@ public class ControllerHandler {
event.getAxisValue(mapping.rightStickYAxis), mapping.rightStickDeadzoneRadius);
rightStickX = (short)(rightStickVector.getX() * 0x7FFE);
rightStickY = (short)(-rightStickVector.getY() * 0x7FFE);
rightStickY = (short)(-rightStickVector.getY() * 0x7FFE);
}
// Handle controllers with analog triggers
@@ -2,6 +2,7 @@ package com.limelight.binding.input.evdev;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import com.limelight.LimeLog;
@@ -14,6 +15,7 @@ public class EvdevWatcher {
private HashMap<String, EvdevHandler> handlers = new HashMap<String, EvdevHandler>();
private boolean shutdown = false;
private boolean init = false;
private boolean ungrabbed = false;
private EvdevListener listener;
private Thread startThread;
@@ -42,7 +44,11 @@ public class EvdevWatcher {
}
EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener);
handler.start();
// If we're ungrabbed now, don't start the handler
if (!ungrabbed) {
handler.start();
}
handlers.put(fileName, handler);
}
@@ -78,6 +84,31 @@ public class EvdevWatcher {
return files;
}
public void ungrabAll() {
synchronized (handlers) {
// Note that we're ungrabbed for now
ungrabbed = true;
// Stop all handlers
for (EvdevHandler handler : handlers.values()) {
handler.stop();
}
}
}
public void regrabAll() {
synchronized (handlers) {
// We're regrabbing everything now
ungrabbed = false;
for (Map.Entry<String, EvdevHandler> entry : handlers.entrySet()) {
// We need to recreate each entry since we can't reuse a stopped one
entry.setValue(new EvdevHandler(PATH + "/" + entry.getKey(), listener));
entry.getValue().start();
}
}
}
public void start() {
startThread = new Thread() {
@Override
@@ -119,10 +150,15 @@ public class EvdevWatcher {
// Stop the observer
observer.stopWatching();
synchronized (handlers) {
synchronized (handlers) {
// Stop creating new handlers
shutdown = true;
// If we've already ungrabbed, there's nothing else to do
if (ungrabbed) {
return;
}
// Stop all handlers
for (EvdevHandler handler : handlers.values()) {
handler.stop();
@@ -38,6 +38,7 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
private int cpuCount = Runtime.getRuntime().availableProcessors();
@SuppressWarnings("unused")
private int findOptimalPerformanceLevel() {
StringBuilder cpuInfo = new StringBuilder();
BufferedReader br = null;
@@ -93,7 +94,7 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
public boolean setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
this.targetFps = redrawRate;
int perfLevel = findOptimalPerformanceLevel();
int perfLevel = LOW_PERF; //findOptimalPerformanceLevel();
int threadCount;
int avcFlags = 0;
@@ -44,11 +44,20 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
private int numPpsIn;
private int numIframeIn;
public static final List<String> preferredDecoders;
public static final List<String> blacklistedDecoderPrefixes;
public static final List<String> spsFixupBitstreamFixupDecoderPrefixes;
public static final List<String> spsFixupNumRefFixupDecoderPrefixes;
public static final List<String> whitelistedAdaptiveResolutionPrefixes;
static {
preferredDecoders = new LinkedList<String>();
// This is the most reliable of Samsung's decoders
preferredDecoders.add("OMX.SEC.AVC.Decoder");
}
static {
blacklistedDecoderPrefixes = new LinkedList<String>();
@@ -158,6 +167,31 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
return str;
}
private static MediaCodecInfo findPreferredDecoder() {
// This is a different algorithm than the other findXXXDecoder functions,
// because we want to evaluate the decoders in our list's order
// rather than MediaCodecList's order
for (String preferredDecoder : preferredDecoders) {
for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
// Skip encoders
if (codecInfo.isEncoder()) {
continue;
}
// Check for preferred decoders
if (preferredDecoder.equalsIgnoreCase(codecInfo.getName())) {
LimeLog.info("Preferred decoder choice is "+codecInfo.getName());
return codecInfo;
}
}
}
return null;
}
private static MediaCodecInfo findFirstDecoder() {
for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
@@ -186,7 +220,13 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
}
public static MediaCodecInfo findProbableSafeDecoder() {
// First look for decoders we know are safe
// First look for a preferred decoder by name
MediaCodecInfo info = findPreferredDecoder();
if (info != null) {
return info;
}
// Now look for decoders we know are safe
try {
// If this function completes, it will determine if the decoder is safe
return findKnownSafeDecoder();
@@ -436,9 +476,9 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
LimeLog.info("Adding bitstream restrictions");
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = false;
sps.vuiParams.bitstreamRestriction.max_bytes_per_pic_denom = 0;
sps.vuiParams.bitstreamRestriction.max_bits_per_mb_denom = 0;
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = true;
sps.vuiParams.bitstreamRestriction.max_bytes_per_pic_denom = 2;
sps.vuiParams.bitstreamRestriction.max_bits_per_mb_denom = 1;
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_horizontal = 16;
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_vertical = 16;
sps.vuiParams.bitstreamRestriction.num_reorder_frames = 0;
@@ -342,7 +342,7 @@ public class ComputerManagerService extends Service {
polledDetails = tryPollIp(details.remoteIp);
}
if (polledDetails == null) {
if (polledDetails == null && !details.localIp.equals(details.remoteIp)) {
// Failed, so let's try the fallback
if (!localFirst) {
polledDetails = tryPollIp(details.localIp);
@@ -356,7 +356,8 @@ public class ComputerManagerService extends Service {
polledDetails.reachability = !localFirst ? ComputerDetails.Reachability.LOCAL :
ComputerDetails.Reachability.REMOTE;
}
} else {
}
else if (polledDetails != null) {
polledDetails.reachability = localFirst ? ComputerDetails.Reachability.LOCAL :
ComputerDetails.Reachability.REMOTE;
}
@@ -401,7 +402,12 @@ public class ComputerManagerService extends Service {
public void run() {
boolean newPc = (details.name == null);
if (stopped) {
// This is called from addComputerManually() where we don't
// want to block the initial poll if polling is disabled, so
// we explicitly let this through if we've never seen this
// PC before. This path won't be triggered normally when polling
// is disabled because the mDNS discovery is stopped.
if (stopped && !newPc) {
return;
}