diff --git a/app/app.iml b/app/app.iml index b022a270..b064e103 100644 --- a/app/app.iml +++ b/app/app.iml @@ -1,5 +1,5 @@ - + @@ -85,30 +85,36 @@ + + + + + + + - + - - - - - + - + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 926c729d..e7cceac1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { minSdkVersion 16 targetSdkVersion 23 - versionName "4.0.2" - versionCode = 79 + versionName "4.0.3" + versionCode = 81 } productFlavors { diff --git a/app/libs/limelight-common.jar b/app/libs/limelight-common.jar index 608d77cd..4100bf35 100644 Binary files a/app/libs/limelight-common.jar and b/app/libs/limelight-common.jar differ diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 0cac438d..e9b1ed62 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -369,8 +369,10 @@ public class Game extends Activity implements SurfaceHolder.Callback, SpinnerDialog.closeDialogs(this); Dialog.closeDialogs(); - InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE); - inputManager.unregisterInputDeviceListener(controllerHandler); + if (controllerHandler != null) { + InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE); + inputManager.unregisterInputDeviceListener(controllerHandler); + } wifiLock.release(); @@ -379,36 +381,38 @@ public class Game extends Activity implements SurfaceHolder.Callback, unbindService(usbDriverServiceConnection); } - VideoDecoderRenderer.VideoFormat videoFormat = conn.getActiveVideoFormat(); + if (conn != null) { + VideoDecoderRenderer.VideoFormat videoFormat = conn.getActiveVideoFormat(); - displayedFailureDialog = true; - stopConnection(); + displayedFailureDialog = true; + stopConnection(); - int averageEndToEndLat = decoderRenderer.getAverageEndToEndLatency(); - int averageDecoderLat = decoderRenderer.getAverageDecoderLatency(); - String message = null; - if (averageEndToEndLat > 0) { - message = getResources().getString(R.string.conn_client_latency)+" "+averageEndToEndLat+" ms"; - if (averageDecoderLat > 0) { - message += " ("+getResources().getString(R.string.conn_client_latency_hw)+" "+averageDecoderLat+" ms)"; + int averageEndToEndLat = decoderRenderer.getAverageEndToEndLatency(); + int averageDecoderLat = decoderRenderer.getAverageDecoderLatency(); + String message = null; + if (averageEndToEndLat > 0) { + message = getResources().getString(R.string.conn_client_latency)+" "+averageEndToEndLat+" ms"; + if (averageDecoderLat > 0) { + message += " ("+getResources().getString(R.string.conn_client_latency_hw)+" "+averageDecoderLat+" ms)"; + } } - } - else if (averageDecoderLat > 0) { - message = getResources().getString(R.string.conn_hardware_latency)+" "+averageDecoderLat+" ms"; - } - - // Add the video codec to the post-stream toast - if (message != null && videoFormat != VideoDecoderRenderer.VideoFormat.Unknown) { - if (videoFormat == VideoDecoderRenderer.VideoFormat.H265) { - message += " [H.265]"; + else if (averageDecoderLat > 0) { + message = getResources().getString(R.string.conn_hardware_latency)+" "+averageDecoderLat+" ms"; } - else { - message += " [H.264]"; - } - } - if (message != null) { - Toast.makeText(this, message, Toast.LENGTH_LONG).show(); + // Add the video codec to the post-stream toast + if (message != null && videoFormat != VideoDecoderRenderer.VideoFormat.Unknown) { + if (videoFormat == VideoDecoderRenderer.VideoFormat.H265) { + message += " [H.265]"; + } + else { + message += " [H.264]"; + } + } + + if (message != null) { + Toast.makeText(this, message, Toast.LENGTH_LONG).show(); + } } finish(); diff --git a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java index 695468c8..1a079cb1 100644 --- a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java +++ b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java @@ -362,7 +362,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD } // Samsung's face buttons appear as a non-virtual button so we'll explicitly ignore // back presses on this device - else if (devName.equals("sec_touchscreen")) { + else if (devName.equals("sec_touchscreen") || devName.equals("sec_touchkey")) { context.ignoreBack = true; } // The Serval has a couple of unknown buttons that are start and select. It also has diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java b/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java index f2a81f1d..81a4d991 100644 --- a/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java +++ b/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java @@ -76,14 +76,31 @@ public class MediaCodecHelper { constrainedHighProfilePrefixes = new LinkedList(); constrainedHighProfilePrefixes.add("omx.intel"); + } + static { whitelistedHevcDecoders = new LinkedList<>(); + + // Exynos seems to be the only HEVC decoder that works reliably whitelistedHevcDecoders.add("omx.exynos"); - // whitelistedHevcDecoders.add("omx.nvidia"); TODO: This needs a similar fixup to the Tegra 3 - whitelistedHevcDecoders.add("omx.mtk"); - whitelistedHevcDecoders.add("omx.amlogic"); - whitelistedHevcDecoders.add("omx.rk"); - // omx.qcom added conditionally during initialization + + // TODO: This needs a similar fixup to the Tegra 3 otherwise it buffers 16 frames + //whitelistedHevcDecoders.add("omx.nvidia"); + + // Sony ATVs have broken MediaTek codecs (decoder hangs after rendering the first frame). + // I know the Fire TV 2 works, so I'll just whitelist Amazon devices which seem + // to actually be tested. Ugh... + if (Build.MANUFACTURER.equalsIgnoreCase("Amazon")) { + whitelistedHevcDecoders.add("omx.mtk"); + } + + // These theoretically have good HEVC decoding capabilities (potentially better than + // their AVC decoders), but haven't been tested enough + //whitelistedHevcDecoders.add("omx.amlogic"); + //whitelistedHevcDecoders.add("omx.rk"); + + // Based on GPU attributes queried at runtime, the omx.qcom prefix will be added + // during initialization to avoid SoCs with broken HEVC decoders. } public static void initializeWithContext(Context context) { diff --git a/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java b/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java index c5c5585d..d3d19867 100644 --- a/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java +++ b/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java @@ -53,7 +53,7 @@ public class ComputerDatabaseManager { } public void deleteComputer(String name) { - computerDb.delete(COMPUTER_TABLE_NAME, COMPUTER_NAME_COLUMN_NAME+"='"+name+"'", null); + computerDb.delete(COMPUTER_TABLE_NAME, COMPUTER_NAME_COLUMN_NAME+"=?", new String[]{name}); } public boolean updateComputer(ComputerDetails details) { @@ -118,7 +118,7 @@ public class ComputerDatabaseManager { } public ComputerDetails getComputerByName(String name) { - Cursor c = computerDb.rawQuery("SELECT * FROM "+COMPUTER_TABLE_NAME+" WHERE "+COMPUTER_NAME_COLUMN_NAME+"='"+name+"'", null); + Cursor c = computerDb.query(COMPUTER_TABLE_NAME, null, COMPUTER_NAME_COLUMN_NAME+"=?", new String[]{name}, null, null, null); ComputerDetails details = new ComputerDetails(); if (!c.moveToFirst()) { // No matching computer diff --git a/app/src/main/jni/evdev_reader/evdev_reader.c b/app/src/main/jni/evdev_reader/evdev_reader.c index 039ce769..bdd6b30d 100644 --- a/app/src/main/jni/evdev_reader/evdev_reader.c +++ b/app/src/main/jni/evdev_reader/evdev_reader.c @@ -14,6 +14,8 @@ #include +#define EVDEV_MAX_EVENT_SIZE 24 + #define REL_X 0x00 #define REL_Y 0x01 #define KEY_Q 16 @@ -69,7 +71,7 @@ void* pollThreadFunc(void* context) { struct DeviceEntry *device = context; struct pollfd pollinfo; int pollres, ret; - char data[64]; + char data[EVDEV_MAX_EVENT_SIZE]; __android_log_print(ANDROID_LOG_INFO, "EvdevReader", "Polling /dev/input/%s", device->devName); @@ -94,7 +96,7 @@ void* pollThreadFunc(void* context) { if (pollres > 0 && (pollinfo.revents & POLLIN)) { // We'll have data available now - ret = read(device->fd, data, sizeof(struct input_event)); + ret = read(device->fd, data, EVDEV_MAX_EVENT_SIZE); if (ret < 0) { __android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "read() failed: %d", errno); @@ -132,6 +134,9 @@ cleanup: { struct DeviceEntry *lastEntry; + // Lock the device list + pthread_mutex_lock(&DeviceListLock); + if (DeviceListHead == device) { DeviceListHead = device->next; } @@ -146,6 +151,9 @@ cleanup: lastEntry = lastEntry->next; } } + + // Unlock device list + pthread_mutex_unlock(&DeviceListLock); } // Free the context @@ -254,6 +262,11 @@ static int enumerateDevices(void) { continue; } + if (strstr(dirEnt->d_name, "event") == NULL) { + // Skip non-event devices + continue; + } + startPollForDevice(dirEnt->d_name); } @@ -269,6 +282,8 @@ int main(int argc, char* argv[]) { int pollres; struct pollfd pollinfo; + __android_log_print(ANDROID_LOG_INFO, "EvdevReader", "Entered main()"); + // Perform initial enumeration ret = enumerateDevices(); if (ret < 0) { @@ -293,33 +308,49 @@ int main(int argc, char* argv[]) { } while (pollres == 0); - ret = fread(&requestId, sizeof(requestId), 1, stdin); - if (ret < sizeof(requestId)) { - __android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "Short read on input"); - return errno; - } - - if (requestId != UNGRAB_REQ && requestId != REGRAB_REQ) { - __android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "Unknown request"); - return requestId; - } - - { - struct DeviceEntry *currentEntry; - - pthread_mutex_lock(&DeviceListLock); - - // Update state for future devices - grabbing = (requestId == REGRAB_REQ); - - // Carry out the requested action on each device - currentEntry = DeviceListHead; - while (currentEntry != NULL) { - ioctl(currentEntry->fd, EVIOCGRAB, grabbing); - currentEntry = currentEntry->next; + if (pollres > 0 && (pollinfo.revents & POLLIN)) { + // We'll have data available now + ret = fread(&requestId, sizeof(requestId), 1, stdin); + if (ret < sizeof(requestId)) { + __android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "Short read on input"); + return errno; } - pthread_mutex_unlock(&DeviceListLock); + if (requestId != UNGRAB_REQ && requestId != REGRAB_REQ) { + __android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "Unknown request"); + return requestId; + } + + { + struct DeviceEntry *currentEntry; + + pthread_mutex_lock(&DeviceListLock); + + // Update state for future devices + grabbing = (requestId == REGRAB_REQ); + + // Carry out the requested action on each device + currentEntry = DeviceListHead; + while (currentEntry != NULL) { + ioctl(currentEntry->fd, EVIOCGRAB, grabbing); + currentEntry = currentEntry->next; + } + + pthread_mutex_unlock(&DeviceListLock); + } + } + else { + // Terminate this thread + if (pollres < 0) { + __android_log_print(ANDROID_LOG_ERROR, "EvdevReader", + "Stdin poll() failed: %d", errno); + } + else { + __android_log_print(ANDROID_LOG_ERROR, "EvdevReader", + "Stdin unexpected revents: %d", pollinfo.revents); + } + + return -1; } } } \ No newline at end of file diff --git a/limelight_vc.iml b/limelight_vc.iml deleted file mode 100644 index 2a022014..00000000 --- a/limelight_vc.iml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - -