Compare commits

..

23 Commits

Author SHA1 Message Date
Cameron Gutman a92bbc7e5a Increment version to 3.1.9 2015-08-18 00:44:01 -07:00
Cameron Gutman fbc921dd07 Update build files 2015-08-18 00:39:01 -07:00
Cameron Gutman 59c6c3d777 Target Android 6.0 SDK 2015-08-18 00:38:49 -07:00
Cameron Gutman e7ab61c8d0 Use jmDNS 3.4.2 which works properly on Android 6.0 2015-08-17 23:14:20 -07:00
Cameron Gutman 7023760782 Use monotonic system time for rendering timestamps. This is required now in Android 6.0 since these timestamps are propagated to the codec. 2015-08-17 18:40:25 -07:00
Cameron Gutman 932ce435b5 Remove unnecessary buffer clear 2015-08-17 18:26:33 -07:00
Cameron Gutman af384d88f7 Handle potentially poisoned bitmap cache caused by truncated images from the server 2015-08-17 17:59:15 -07:00
Cameron Gutman 792846ddad Update MediaCodec renderer to avoid deprecated features on Lollipop 2015-08-17 17:52:57 -07:00
Cameron Gutman 1187d9c78c Update libraries 2015-08-14 09:29:56 -07:00
Cameron Gutman 37db9ab072 Update common with latest fixes 2015-08-12 00:39:07 -07:00
Cameron Gutman fb40060560 Upgrade common jar to incorporate IDR frame fix 2015-08-04 23:46:20 -07:00
Cameron Gutman a4f4887647 Upgrade build tools and libraries 2015-08-04 23:46:03 -07:00
Cameron Gutman f1d7f556fd Bump to version 3.1.8 2015-07-21 18:03:37 -07:00
Cameron Gutman 1e70e1d329 GFE 2.5.11 update to fix black screen on Fire TV Stick 2015-07-18 17:06:41 -07:00
Cameron Gutman e02a009635 Add support for the Razer Serval controller. The start and select buttons are manually handled for devices without a mapping for them. The back button is ignored so it can be used to exit the stream. 2015-07-18 00:46:25 -07:00
Cameron Gutman bd6ff35603 Update to 3.1.7 2015-06-15 10:37:58 -07:00
Cameron Gutman 1cb7727dc7 Update common 2015-06-15 10:28:31 -07:00
Cameron Gutman 0c73e3d0ae Only propagate a decoder exception if it happens at the beginning of a stream 2015-06-15 10:28:09 -07:00
Cameron Gutman 6371d364e1 Lint warning cleanup 2015-05-29 23:22:40 -05:00
Cameron Gutman ded9c9140d Handle being online but not having a known reachability 2015-05-29 23:20:04 -05:00
Cameron Gutman 7c8a108e28 Use the leanback feature on API 21+ devices 2015-05-29 23:18:56 -05:00
Cameron Gutman 2a18ffcdba Update to Gradle 1.2.3 2015-05-19 10:10:18 -05:00
Cameron Gutman 381d0d5e81 Add support for multi-window functionality on Samsung devices 2015-05-10 00:02:04 -05:00
19 changed files with 189 additions and 99 deletions
+13 -11
View File
@@ -12,10 +12,12 @@
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleNonRootDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileNonRootDebugSources" />
<option name="SOURCE_GEN_TASK_NAME" value="generateNonRootDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleNonRootDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileNonRootDebugAndroidTestSources" />
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateNonRootDebugAndroidTestSources" />
<afterSyncTasks>
<task>generateNonRootDebugAndroidTestSources</task>
<task>generateNonRootDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
@@ -24,7 +26,7 @@
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/nonRoot/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/nonRoot/debug" />
<exclude-output />
@@ -34,7 +36,7 @@
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/nonRoot/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/nonRoot/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/nonRoot/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/nonRoot/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/nonRoot/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/assets" type="java-resource" />
@@ -47,7 +49,7 @@
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/nonRoot/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/nonRoot/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/nonRoot/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/assets" type="java-resource" />
@@ -104,15 +106,15 @@
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="bcprov-jdk15on-1.51" level="project" />
<orderEntry type="library" exported="" name="jmdns-fixed" level="project" />
<orderEntry type="library" exported="" name="bcpkix-jdk15on-1.51" level="project" />
<orderEntry type="library" exported="" name="bcprov-jdk15on-1.52" level="project" />
<orderEntry type="library" exported="" name="bcpkix-jdk15on-1.52" level="project" />
<orderEntry type="library" exported="" name="tinyrtsp" level="project" />
<orderEntry type="library" exported="" name="limelight-common" level="project" />
<orderEntry type="library" exported="" name="okhttp-2.2.0" level="project" />
<orderEntry type="library" exported="" name="jmdns-3.4.2" level="project" />
<orderEntry type="library" exported="" name="okhttp-2.4.0" level="project" />
<orderEntry type="library" exported="" name="jcodec-0.1.9" level="project" />
<orderEntry type="library" exported="" name="okio-1.2.0" level="project" />
<orderEntry type="library" exported="" name="okio-1.5.0" level="project" />
</component>
</module>
+10 -10
View File
@@ -4,15 +4,15 @@ import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "21.1.2"
compileSdkVersion 23
buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 22
targetSdkVersion 23
versionName "3.1.6"
versionCode = 61
versionName "3.1.9"
versionCode = 64
}
productFlavors {
@@ -64,13 +64,13 @@ android {
dependencies {
compile group: 'org.jcodec', name: 'jcodec', version: '0.1.9'
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.51'
compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.51'
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.52'
compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.52'
compile group: 'com.squareup.okhttp', name: 'okhttp', version:'2.2.0'
compile group: 'com.squareup.okio', name:'okio', version:'1.2.0'
compile group: 'com.squareup.okhttp', name: 'okhttp', version:'2.4.0'
compile group: 'com.squareup.okio', name:'okio', version:'1.5.0'
compile files('libs/jmdns-fixed.jar')
compile files('libs/jmdns-3.4.2.jar')
compile files('libs/limelight-common.jar')
compile files('libs/tinyrtsp.jar')
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5
View File
@@ -16,6 +16,10 @@
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:theme="@style/AppTheme" >
<!-- Samsung multi-window support -->
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<!-- Launcher for traditional devices -->
<activity
@@ -24,6 +28,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
<category android:name="tv.ouya.intent.category.APP" />
</intent-filter>
</activity>
@@ -1,19 +1,14 @@
package com.limelight;
import java.io.FileNotFoundException;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import com.limelight.binding.PlatformBinding;
import com.limelight.computers.ComputerManagerListener;
import com.limelight.computers.ComputerManagerService;
import com.limelight.grid.AppGridAdapter;
import com.limelight.nvstream.http.ComputerDetails;
import com.limelight.nvstream.http.GfeHttpResponseException;
import com.limelight.nvstream.http.NvApp;
import com.limelight.nvstream.http.NvHTTP;
import com.limelight.preferences.PreferenceConfiguration;
@@ -26,10 +21,8 @@ import com.limelight.utils.SpinnerDialog;
import com.limelight.utils.UiHelper;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Service;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
@@ -306,6 +306,10 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
else if (computer.reachability == ComputerDetails.Reachability.REMOTE) {
addr = computer.remoteIp;
}
else {
LimeLog.warning("Unknown reachability - using local IP");
addr = computer.localIp;
}
httpConn = new NvHTTP(addr,
managerBinder.getUniqueId(),
@@ -431,6 +435,10 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
else if (computer.reachability == ComputerDetails.Reachability.REMOTE) {
addr = computer.remoteIp;
}
else {
LimeLog.warning("Unknown reachability - using local IP");
addr = computer.localIp;
}
httpConn = new NvHTTP(addr,
managerBinder.getUniqueId(),
@@ -1,7 +1,5 @@
package com.limelight.binding.input;
import java.util.Map;
import android.hardware.input.InputManager;
import android.os.SystemClock;
import android.util.SparseArray;
@@ -52,8 +50,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
deadzonePercentage = 10;
int[] ids = InputDevice.getDeviceIds();
for (int i = 0; i < ids.length; i++) {
InputDevice dev = InputDevice.getDevice(ids[i]);
for (int id : ids) {
InputDevice dev = InputDevice.getDevice(id);
if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 ||
(dev.getSources() & InputDevice.SOURCE_GAMEPAD) != 0) {
// This looks like a gamepad, but we'll check X and Y to be sure
@@ -275,6 +273,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
}
}
// Ignore the back buttonn if a controller has both buttons
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
boolean[] hasSelectKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BACK, 0);
if (hasSelectKey[0] && hasSelectKey[1]) {
LimeLog.info("Ignoring back button because select is present");
context.ignoreBack = true;
}
}
if (devName != null) {
// For the Nexus Player (and probably other ATV devices), we should
// use the back button as start since it doesn't have a start/menu button
@@ -298,7 +305,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
else if (devName.contains("Fire TV Remote") || devName.contains("Nexus Remote")) {
// It's only a remote if it doesn't any sticks
if (!context.hasJoystickAxes) {
context.isRemote = true;
context.ignoreBack = true;
}
}
// SHIELD controllers will use small stick deadzones
@@ -306,10 +313,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
context.leftStickDeadzoneRadius = 0.07f;
context.rightStickDeadzoneRadius = 0.07f;
}
// Samsung's face buttons appear as a non-virtual button so we'll classify them as remotes
// so the back button gets passed through to exit streaming
// 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")) {
context.isRemote = true;
context.ignoreBack = true;
}
// The Serval has a couple of unknown buttons that are start and select. It also has
// a back button which we want to ignore since there's already a select button.
else if (devName.contains("Razer Serval")) {
context.isServal = true;
context.ignoreBack = true;
}
}
@@ -349,8 +362,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
// Return a valid keycode, 0 to consume, or -1 to not consume the event
// Device MAY BE NULL
private int handleRemapping(ControllerContext context, KeyEvent event) {
// For remotes, don't capture the back button
if (context.isRemote) {
// Don't capture the back button if configured
if (context.ignoreBack) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
return -1;
}
@@ -394,6 +407,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
return 0;
}
}
// If this is a Serval controller sending an unknown key code, it's probably
// the start and select buttons
else if (context.isServal && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
switch (event.getScanCode()) {
case 314:
return KeyEvent.KEYCODE_BUTTON_SELECT;
case 315:
return KeyEvent.KEYCODE_BUTTON_START;
}
}
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
switch (event.getKeyCode()) {
@@ -815,9 +838,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
public boolean isDualShock4;
public boolean isXboxController;
public boolean isServal;
public boolean backIsStart;
public boolean modeIsSelect;
public boolean isRemote;
public boolean ignoreBack;
public boolean hasJoystickAxes;
public boolean assignedControllerNumber;
@@ -177,10 +177,10 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
rendererThread = new Thread() {
@Override
public void run() {
long nextFrameTime = System.currentTimeMillis();
long nextFrameTime = MediaCodecHelper.getMonotonicMillis();
while (!isInterrupted())
{
long diff = nextFrameTime - System.currentTimeMillis();
long diff = nextFrameTime - MediaCodecHelper.getMonotonicMillis();
if (diff > WAIT_CEILING_MS) {
try {
@@ -203,7 +203,7 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
}
private long computePresentationTimeMs(int frameRate) {
return System.currentTimeMillis() + (1000 / frameRate);
return MediaCodecHelper.getMonotonicMillis() + (1000 / frameRate);
}
@Override
@@ -251,7 +251,7 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
boolean success = (AvcDecoder.decode(data, 0, decodeUnit.getDataLength()) == 0);
if (success) {
long timeAfterDecode = System.currentTimeMillis();
long timeAfterDecode = MediaCodecHelper.getMonotonicMillis();
// Add delta time to the totals (excluding probable outliers)
long delta = timeAfterDecode - decodeUnit.getReceiveTimestamp();
@@ -13,7 +13,6 @@ import com.limelight.nvstream.av.DecodeUnit;
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
import com.limelight.nvstream.av.video.VideoDepacketizer;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
@@ -22,10 +21,11 @@ import android.media.MediaCodec.CodecException;
import android.os.Build;
import android.view.SurfaceHolder;
@SuppressWarnings("unused")
public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
private ByteBuffer[] videoDecoderInputBuffers;
// Used on versions < 5.0
private ByteBuffer[] legacyInputBuffers;
private MediaCodec videoDecoder;
private Thread rendererThread;
private final boolean needsSpsBitstreamFixup, isExynos4;
@@ -46,7 +46,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
private int numPpsIn;
private int numIframeIn;
@TargetApi(Build.VERSION_CODES.KITKAT)
public MediaCodecDecoderRenderer() {
//dumpDecoders();
@@ -118,7 +117,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
return true;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void handleDecoderException(Exception e, ByteBuffer buf, int codecFlags) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (e instanceof CodecException) {
@@ -134,18 +132,20 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
}
}
if (buf != null || codecFlags != 0) {
throw new RendererException(this, e, buf, codecFlags);
}
else {
throw new RendererException(this, e);
// Only throw if this happens at the beginning of a stream
if (totalFrames < 60) {
if (buf != null || codecFlags != 0) {
throw new RendererException(this, e, buf, codecFlags);
}
else {
throw new RendererException(this, e);
}
}
}
private void startDirectSubmitRendererThread()
{
rendererThread = new Thread() {
@SuppressWarnings("deprecation")
@Override
public void run() {
BufferInfo info = new BufferInfo();
@@ -169,7 +169,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
videoDecoder.releaseOutputBuffer(lastIndex, true);
// Add delta time to the totals (excluding probable outliers)
long delta = System.currentTimeMillis() - (presentationTimeUs / 1000);
long delta = MediaCodecHelper.getMonotonicMillis() - (presentationTimeUs / 1000);
if (delta >= 0 && delta < 1000) {
decoderTimeMs += delta;
totalTimeMs += delta;
@@ -178,9 +178,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
switch (outIndex) {
case MediaCodec.INFO_TRY_AGAIN_LATER:
break;
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
LimeLog.info("Output buffers changed");
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
LimeLog.info("Output format changed");
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
@@ -204,17 +201,17 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
int index;
long startTime, queueTime;
startTime = System.currentTimeMillis();
startTime = MediaCodecHelper.getMonotonicMillis();
index = videoDecoder.dequeueInputBuffer(wait ? (infiniteWait ? -1 : 3000) : 0);
if (index < 0) {
return index;
}
queueTime = System.currentTimeMillis();
queueTime = MediaCodecHelper.getMonotonicMillis();
if (queueTime - startTime >= 20) {
LimeLog.warning("Queue input buffer ran long: "+(queueTime - startTime)+" ms");
LimeLog.warning("Queue input buffer ran long: " + (queueTime - startTime) + " ms");
}
return index;
@@ -223,7 +220,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
private void startLegacyRendererThread()
{
rendererThread = new Thread() {
@SuppressWarnings("deprecation")
@Override
public void run() {
BufferInfo info = new BufferInfo();
@@ -240,7 +236,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
inputIndex = dequeueInputBuffer(false, false);
du = depacketizer.pollNextDecodeUnit();
if (du != null) {
lastDuDequeueTime = System.currentTimeMillis();
lastDuDequeueTime = MediaCodecHelper.getMonotonicMillis();
notifyDuReceived(du);
}
@@ -249,7 +245,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
break;
}
submitDecodeUnit(du, videoDecoderInputBuffers[inputIndex], inputIndex);
submitDecodeUnit(du, inputIndex);
du = null;
inputIndex = -1;
@@ -282,7 +278,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
if (du == null) {
du = depacketizer.pollNextDecodeUnit();
if (du != null) {
lastDuDequeueTime = System.currentTimeMillis();
lastDuDequeueTime = MediaCodecHelper.getMonotonicMillis();
notifyDuReceived(du);
}
}
@@ -290,12 +286,12 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
// If we've got both a decode unit and an input buffer, we'll
// submit now. Otherwise, we wait until we have one.
if (du != null && inputIndex >= 0) {
long submissionTime = System.currentTimeMillis();
long submissionTime = MediaCodecHelper.getMonotonicMillis();
if (submissionTime - lastDuDequeueTime >= 20) {
LimeLog.warning("Receiving an input buffer took too long: "+(submissionTime - lastDuDequeueTime)+" ms");
}
submitDecodeUnit(du, videoDecoderInputBuffers[inputIndex], inputIndex);
submitDecodeUnit(du, inputIndex);
// DU and input buffer have both been consumed
du = null;
@@ -321,7 +317,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
videoDecoder.releaseOutputBuffer(lastIndex, true);
// Add delta time to the totals (excluding probable outliers)
long delta = System.currentTimeMillis()-(presentationTimeUs/1000);
long delta = MediaCodecHelper.getMonotonicMillis()-(presentationTimeUs/1000);
if (delta >= 0 && delta < 1000) {
decoderTimeMs += delta;
totalTimeMs += delta;
@@ -335,9 +331,6 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
LockSupport.parkNanos(1);
}
break;
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
LimeLog.info("Output buffers changed");
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
LimeLog.info("Output format changed");
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
@@ -365,7 +358,9 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
// Start the decoder
videoDecoder.start();
videoDecoderInputBuffers = videoDecoder.getInputBuffers();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
legacyInputBuffers = videoDecoder.getInputBuffers();
}
if (directSubmit) {
startDirectSubmitRendererThread();
@@ -406,7 +401,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
for (i = 0; i < 25; i++) {
try {
videoDecoder.queueInputBuffer(inputBufferIndex,
0, length,
offset, length,
timestampUs, codecFlags);
break;
} catch (Exception e) {
@@ -420,9 +415,27 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
}
}
// Using the new getInputBuffer() API on Lollipop allows
// the framework to do some performance optimizations for us
private ByteBuffer getEmptyInputBuffer(int inputBufferIndex) {
ByteBuffer buf;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
buf = videoDecoder.getInputBuffer(inputBufferIndex);
}
else {
buf = legacyInputBuffers[inputBufferIndex];
// Clear old input data pre-Lollipop
buf.clear();
}
return buf;
}
@SuppressWarnings("deprecation")
private void submitDecodeUnit(DecodeUnit decodeUnit, ByteBuffer buf, int inputBufferIndex) {
long timestampUs = System.currentTimeMillis() * 1000;
private void submitDecodeUnit(DecodeUnit decodeUnit, int inputBufferIndex) {
long timestampUs = System.nanoTime() / 1000;
if (timestampUs <= lastTimestampUs) {
// We can't submit multiple buffers with the same timestamp
// so bump it up by one before queuing
@@ -430,8 +443,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
}
lastTimestampUs = timestampUs;
// Clear old input data
buf.clear();
ByteBuffer buf = getEmptyInputBuffer(inputBufferIndex);
int codecFlags = 0;
int decodeUnitFlags = decodeUnit.getFlags();
@@ -483,19 +495,49 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
LimeLog.info("Patching num_ref_frames in SPS");
sps.num_ref_frames = 1;
// GFE 2.5.11 changed the SPS to add additional extensions
// Some devices don't like these so we remove them here.
sps.vuiParams.video_signal_type_present_flag = false;
sps.vuiParams.colour_description_present_flag = false;
sps.vuiParams.colour_primaries = 2;
sps.vuiParams.transfer_characteristics = 2;
sps.vuiParams.matrix_coefficients = 2;
sps.vuiParams.chroma_loc_info_present_flag = false;
sps.vuiParams.chroma_sample_loc_type_bottom_field = 0;
sps.vuiParams.chroma_sample_loc_type_top_field = 0;
if (needsSpsBitstreamFixup || isExynos4) {
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
// or max_dec_frame_buffering which increases decoding latency on Tegra.
LimeLog.info("Adding bitstream restrictions");
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = true;
// GFE 2.5.11 started sending bitstream restrictions
if (sps.vuiParams.bitstreamRestriction == null) {
LimeLog.info("Adding bitstream restrictions");
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = true;
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;
}
else {
LimeLog.info("Patching bitstream restrictions");
}
// Some devices throw errors if max_dec_frame_buffering < num_ref_frames
sps.vuiParams.bitstreamRestriction.max_dec_frame_buffering = sps.num_ref_frames;
// These values are the defaults for the fields, but they are more aggressive
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
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;
sps.vuiParams.bitstreamRestriction.max_dec_frame_buffering = 1;
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
// conservative values by GFE 2.5.11. We'll let those values stand.
}
else {
// Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11
// will continue to not receive them now
sps.vuiParams.bitstreamRestriction = null;
}
// If we need to hack this SPS to say we're baseline, do so now
@@ -549,9 +591,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
private void replaySps() {
int inputIndex = dequeueInputBuffer(true, true);
ByteBuffer inputBuffer = videoDecoderInputBuffers[inputIndex];
inputBuffer.clear();
ByteBuffer inputBuffer = getEmptyInputBuffer(inputIndex);
// Write the Annex B header
inputBuffer.put(new byte[]{0x00, 0x00, 0x00, 0x01, 0x67});
@@ -568,7 +608,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
// Queue the new SPS
queueInputBuffer(inputIndex,
0, inputBuffer.position(),
System.currentTimeMillis() * 1000,
System.nanoTime() / 1000,
MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
LimeLog.info("SPS replay complete");
@@ -609,7 +649,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
}
private void notifyDuReceived(DecodeUnit du) {
long currentTime = System.currentTimeMillis();
long currentTime = MediaCodecHelper.getMonotonicMillis();
long delta = currentTime-du.getReceiveTimestamp();
if (delta >= 0 && delta < 1000) {
totalTimeMs += currentTime-du.getReceiveTimestamp();
@@ -633,7 +673,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
}
if (inputIndex >= 0) {
submitDecodeUnit(du, videoDecoderInputBuffers[inputIndex], inputIndex);
submitDecodeUnit(du, inputIndex);
}
}
@@ -83,6 +83,10 @@ public class MediaCodecHelper {
return false;
}
public static long getMonotonicMillis() {
return System.nanoTime() / 1000000L;
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public static boolean decoderSupportsAdaptivePlayback(String decoderName, MediaCodecInfo decoderInfo) {
@@ -393,7 +393,7 @@ public class ComputerManagerService extends Service {
private boolean fastPollIp(InetAddress addr) {
Socket s = new Socket();
try {
s.connect(new InetSocketAddress(addr, NvHTTP.PORT), FAST_POLL_TIMEOUT);
s.connect(new InetSocketAddress(addr, NvHTTP.HTTPS_PORT), FAST_POLL_TIMEOUT);
s.close();
return true;
} catch (IOException e) {
@@ -639,7 +639,7 @@ public class ComputerManagerService extends Service {
if (cacheOut != null) {
cacheOut.close();
}
} catch (IOException e) {}
} catch (IOException ignored) {}
}
// Update the computer
@@ -27,7 +27,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
private final CachedAppAssetLoader loader;
public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) throws KeyManagementException, NoSuchAlgorithmException {
public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) {
super(activity, listMode ? R.layout.simple_row : (small ? R.layout.app_grid_item_small : R.layout.app_grid_item), R.drawable.image_loading);
int dpi = activity.getResources().getDisplayMetrics().densityDpi;
@@ -105,7 +105,11 @@ public class CachedAppAssetLoader {
// If there's a task associated with this load, we should return the bitmap
if (task != null) {
return diskLoader.loadBitmapFromCache(tuple, (int) scalingDivider);
// If the cached bitmap is valid, return it. Otherwise, we'll try the load again
Bitmap bmp = diskLoader.loadBitmapFromCache(tuple, (int) scalingDivider);
if (bmp != null) {
return bmp;
}
}
else {
// Otherwise it's a background load and we return nothing
@@ -26,7 +26,7 @@ public class NetworkAssetLoader {
InputStream in = null;
try {
in = http.getBoxArt(tuple.app);
} catch (IOException e) {}
} catch (IOException ignored) {}
if (in != null) {
LimeLog.info("Network asset load complete: " + tuple);
@@ -3,6 +3,7 @@ package com.limelight.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.preference.PreferenceManager;
public class PreferenceConfiguration {
@@ -69,9 +70,18 @@ public class PreferenceConfiguration {
public static boolean getDefaultSmallMode(Context context) {
PackageManager manager = context.getPackageManager();
if (manager != null && manager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
if (manager != null) {
// TVs shouldn't use small mode by default
return false;
if (manager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
return false;
}
// API 21 uses LEANBACK instead of TELEVISION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
if (manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
return false;
}
}
}
// Use small mode on anything smaller than a 7" tablet
+1 -1
View File
@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.2'
classpath 'com.android.tools.build:gradle:1.3.0'
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />