Compare commits

..
26 Commits
Author SHA1 Message Date
Cameron Gutman 08cc93d337 Update common and increment version 2014-08-29 16:06:55 -07:00
Cameron Gutman 5fdd9b773c Workaround decoder errata of the Exynos 4 2014-08-29 15:38:08 -07:00
Cameron Gutman e3e7ac1e68 Remove rounding on triggers 2014-08-20 22:14:03 -07:00
Cameron Gutman 10212bd38b Fix potential integer overflow issue with stick axes 2014-08-20 22:12:45 -07:00
Cameron Gutman 4bec02f47f Implement a scaled radial deadzone rather than our previous dumb axial deadzones 2014-08-20 22:10:59 -07:00
Cameron Gutman 79f888fe47 Update common 2014-08-09 03:41:03 -07:00
Cameron Gutman 9393bf7f79 Fix a regression in video scaling after the CPU decoding fix 2014-08-06 22:33:41 -07:00
Cameron Gutman 1b1d4399a9 Bump version and update common 2014-08-06 17:24:21 -07:00
Cameron Gutman b2ad259a7c Also add bitstream restrictions to Qualcomm devices to hopefully address the GS3 issue 2014-08-06 17:22:41 -07:00
Cameron Gutman ac5c264090 Only close spinner dialogs corresponding to the current activity 2014-08-06 15:05:26 -07:00
Cameron Gutman fcfdd4e323 Add missing Cursor.close() calls to fix a crash reported on Ouya 2014-08-06 15:01:18 -07:00
Cameron Gutman ea65bb2c0a Remove the extra app list update when the activity is first started 2014-08-06 14:30:19 -07:00
Cameron Gutman b340055588 If the activity is being finished, don't dismiss the dialog. This is already handled in the closeDialogs() function. 2014-08-06 14:26:13 -07:00
Cameron Gutman 2918039b6f Fix a bug where we'd add a null entry to the computer list 2014-08-05 23:44:04 -07:00
Cameron Gutman e8fd1f262a Update common jar 2014-08-05 23:19:37 -07:00
Cameron Gutman 8887401644 Fix surface sizing with scaling disabled on the CPU decoder 2014-08-05 23:16:02 -07:00
Cameron Gutman f892db6ee8 Fix ANR when switching from the PC view to the app view 2014-08-05 22:30:11 -07:00
Cameron Gutman 829532c572 Some devices don't properly get interrupted while waiting for an input buffer, so wait 100 ms then check the interrupt flag 2014-08-05 21:29:21 -07:00
Cameron Gutman 875eb1e773 Update common and version 2014-07-31 15:23:31 -07:00
Cameron Gutman f295289774 Increment version 2014-07-31 01:41:30 -07:00
Cameron Gutman 1f69b4f271 Update to new common 2014-07-31 01:33:32 -07:00
Cameron Gutman 20a5a844db Fix crash with WoL if a MAC address isn't present 2014-07-31 01:32:59 -07:00
Cameron Gutman 1a7a2f848e Don't close spinner dialogs when the AppList page is destroyed. Increment version 2014-07-22 00:16:35 -07:00
Cameron Gutman 887dd9aa21 Add GFE settings optimization checkbox. Move Add PC button to the main screen. 2014-07-21 23:58:20 -07:00
Cameron Gutman e1e4ccf318 Fix Shield's hardware back button 2014-07-21 23:57:28 -07:00
Cameron Gutman ee6edd2404 Allow up to 4 concurrent PC queries 2014-07-21 22:28:11 -07:00
18 changed files with 424 additions and 282 deletions

No files matched your search

+3 -3
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.limelight" package="com.limelight"
android:versionCode="26" android:versionCode="31"
android:versionName="2.5.1" > android:versionName="2.5.3.1" >
<uses-sdk <uses-sdk
android:minSdkVersion="16" android:minSdkVersion="16"
@@ -59,7 +59,7 @@
android:label="Add Computer Manually" > android:label="Add Computer Manually" >
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="com.limelight.StreamSettings" /> android:value="com.limelight.PcView" />
</activity> </activity>
<activity <activity
android:name="com.limelight.Game" android:name="com.limelight.Game"
+5 -2
View File
@@ -1,10 +1,13 @@
This file serves to document some of the decoder errata when using MediaCodec hardware decoders on certain devices. This file serves to document some of the decoder errata when using MediaCodec hardware decoders on certain devices.
1. num_ref_frames is set to 16 by NVENC which causes decoders to allocate 16+ buffers. This can cause an OOM error on some devices. 1. num_ref_frames is set to 16 by NVENC which causes decoders to allocate 16+ buffers. This can cause an OOM error on some devices.
- Affected decoders: TI OMAP4, possibly some Qualcomm chips too (Galaxy S3 on 4.3+) - Affected decoders: TI OMAP4
2. Some decoders have a huge per-frame latency with the unmodified SPS sent from NVENC. Setting max_dec_frame_buffering fixes this latency issue. 2. Some decoders have a huge per-frame latency with the unmodified SPS sent from NVENC. Setting max_dec_frame_buffering fixes this latency issue.
- Affected decoders: NVIDIA Tegra 3 and 4 - Affected decoders: NVIDIA Tegra 3 and 4
3. Some decoders strictly require that you pass BUFFER_FLAG_CODEC_CONFIG and crash upon the IDR frame if you don't 3. Some decoders strictly require that you pass BUFFER_FLAG_CODEC_CONFIG and crash upon the IDR frame if you don't
- Affected decoders: TI OMAP4 - Affected decoders: TI OMAP4
4. Some decoders require num_ref_frames=1 and max_dec_frame_buffering=1 to avoid crashing on SPS or first I-frame
- Affected decoders: Qualcomm in GS3 on 4.3+, Exynos 4
+11 -10
View File
@@ -39,26 +39,27 @@ or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>na
} }
public static final class id { public static final class id {
public static final int addPc=0x7f080001; public static final int addPc=0x7f080001;
public static final int advancedSettingsButton=0x7f080012; public static final int advancedSettingsButton=0x7f080013;
public static final int appListText=0x7f080009; public static final int appListText=0x7f080009;
public static final int autoDec=0x7f080004; public static final int autoDec=0x7f080004;
public static final int bitrateLabel=0x7f080006; public static final int bitrateLabel=0x7f080006;
public static final int bitrateSeekBar=0x7f080007; public static final int bitrateSeekBar=0x7f080007;
public static final int config1080p30Selected=0x7f080010; public static final int config1080p30Selected=0x7f080011;
public static final int config1080p60Selected=0x7f080011; public static final int config1080p60Selected=0x7f080012;
public static final int config720p30Selected=0x7f08000e; public static final int config720p30Selected=0x7f08000f;
public static final int config720p60Selected=0x7f08000f; public static final int config720p60Selected=0x7f080010;
public static final int decoderConfigGroup=0x7f080002; public static final int decoderConfigGroup=0x7f080002;
public static final int discoveryText=0x7f08000b; public static final int discoveryText=0x7f08000b;
public static final int enableSops=0x7f080014;
public static final int hardwareDec=0x7f080005; public static final int hardwareDec=0x7f080005;
public static final int hostTextView=0x7f080000; public static final int hostTextView=0x7f080000;
public static final int manuallyAddPc=0x7f080014; public static final int manuallyAddPc=0x7f08000c;
public static final int pcListView=0x7f080008; public static final int pcListView=0x7f080008;
public static final int rowTextView=0x7f080015; public static final int rowTextView=0x7f080016;
public static final int settingsButton=0x7f08000c; public static final int settingsButton=0x7f08000d;
public static final int softwareDec=0x7f080003; public static final int softwareDec=0x7f080003;
public static final int streamConfigGroup=0x7f08000d; public static final int streamConfigGroup=0x7f08000e;
public static final int stretchToFill=0x7f080013; public static final int stretchToFill=0x7f080015;
public static final int surfaceView=0x7f08000a; public static final int surfaceView=0x7f08000a;
} }
public static final class layout { public static final class layout {
Binary file not shown.
+1 -1
View File
@@ -29,7 +29,7 @@
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:paddingTop="10dp" android:paddingTop="0dp"
android:paddingBottom="10dp" android:paddingBottom="10dp"
android:text="Applications" /> android:text="Applications" />
+10 -2
View File
@@ -29,8 +29,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_below="@+id/settingsButton" android:layout_below="@+id/manuallyAddPc"
android:paddingTop="20dp" android:paddingTop="10dp"
android:paddingBottom="10dp" android:paddingBottom="10dp"
android:text="Discovered PC List" /> android:text="Discovered PC List" />
@@ -41,5 +41,13 @@
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:text="Streaming Settings" /> android:text="Streaming Settings" />
<Button
android:id="@+id/manuallyAddPc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/settingsButton"
android:layout_centerHorizontal="true"
android:text="Add PC Manually" />
</RelativeLayout> </RelativeLayout>
+9 -9
View File
@@ -54,18 +54,10 @@
android:id="@+id/advancedSettingsButton" android:id="@+id/advancedSettingsButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/stretchToFill" android:layout_below="@+id/enableSops"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:text="Advanced Settings" /> android:text="Advanced Settings" />
<Button
android:id="@+id/manuallyAddPc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/advancedSettingsButton"
android:layout_centerHorizontal="true"
android:text="Add PC Manually" />
<CheckBox <CheckBox
android:id="@+id/stretchToFill" android:id="@+id/stretchToFill"
@@ -74,6 +66,14 @@
android:layout_below="@+id/streamConfigGroup" android:layout_below="@+id/streamConfigGroup"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:text="Stretch video to fill screen" /> android:text="Stretch video to fill screen" />
<CheckBox
android:id="@+id/enableSops"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/stretchToFill"
android:layout_marginTop="15dp"
android:text="Allow GFE to modify game settings for optimal streaming" />
</RelativeLayout> </RelativeLayout>
+40 -6
View File
@@ -39,6 +39,7 @@ public class AppView extends Activity {
private final static int RESUME_ID = 1; private final static int RESUME_ID = 1;
private final static int QUIT_ID = 2; private final static int QUIT_ID = 2;
private final static int CANCEL_ID = 3;
public final static String ADDRESS_EXTRA = "Address"; public final static String ADDRESS_EXTRA = "Address";
public final static String UNIQUEID_EXTRA = "UniqueId"; public final static String UNIQUEID_EXTRA = "UniqueId";
@@ -80,8 +81,8 @@ public class AppView extends Activity {
return; return;
} }
// Only open the context menu if it's running, otherwise start it // Only open the context menu if something is running, otherwise start it
if (app.app.getIsRunning()) { if (getRunningAppId() != -1) {
openContextMenu(arg1); openContextMenu(arg1);
} }
else { else {
@@ -90,15 +91,14 @@ public class AppView extends Activity {
} }
}); });
registerForContextMenu(appList); registerForContextMenu(appList);
updateAppList();
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
SpinnerDialog.closeDialogs(this);
Dialog.closeDialogs(); Dialog.closeDialogs();
SpinnerDialog.closeDialogs();
} }
@Override @Override
@@ -108,12 +108,43 @@ public class AppView extends Activity {
updateAppList(); updateAppList();
} }
private int getRunningAppId() {
int runningAppId = -1;
for (int i = 0; i < appListAdapter.getCount(); i++) {
AppObject app = appListAdapter.getItem(i);
if (app.app == null) {
continue;
}
if (app.app.getIsRunning()) {
runningAppId = app.app.getAppId();
break;
}
}
return runningAppId;
}
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
menu.add(Menu.NONE, RESUME_ID, 1, "Resume Session"); AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
menu.add(Menu.NONE, QUIT_ID, 2, "Quit Session"); AppObject selectedApp = (AppObject) appListAdapter.getItem(info.position);
if (selectedApp == null || selectedApp.app == null) {
return;
}
int runningAppId = getRunningAppId();
if (runningAppId != -1) {
if (runningAppId == selectedApp.app.getAppId()) {
menu.add(Menu.NONE, RESUME_ID, 1, "Resume Session");
menu.add(Menu.NONE, QUIT_ID, 2, "Quit Session");
}
else {
menu.add(Menu.NONE, RESUME_ID, 1, "Quit Current Game and Start");
menu.add(Menu.NONE, CANCEL_ID, 2, "Cancel");
}
}
} }
@Override @Override
@@ -135,6 +166,9 @@ public class AppView extends Activity {
doQuit(app.app); doQuit(app.app);
return true; return true;
case CANCEL_ID:
return true;
default: default:
return super.onContextItemSelected(item); return super.onContextItemSelected(item);
} }
+29 -32
View File
@@ -79,7 +79,8 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
public static final String REFRESH_RATE_PREF_STRING = "FPS"; public static final String REFRESH_RATE_PREF_STRING = "FPS";
public static final String DECODER_PREF_STRING = "Decoder"; public static final String DECODER_PREF_STRING = "Decoder";
public static final String BITRATE_PREF_STRING = "Bitrate"; public static final String BITRATE_PREF_STRING = "Bitrate";
public static final String STRETCH_PREF_STRING = "Stretch"; public static final String STRETCH_PREF_STRING = "Stretch";
public static final String SOPS_PREF_STRING = "Sops";
public static final int BITRATE_DEFAULT_720_30 = 5; public static final int BITRATE_DEFAULT_720_30 = 5;
public static final int BITRATE_DEFAULT_720_60 = 10; public static final int BITRATE_DEFAULT_720_60 = 10;
@@ -92,6 +93,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
public static final int DEFAULT_DECODER = 0; public static final int DEFAULT_DECODER = 0;
public static final int DEFAULT_BITRATE = BITRATE_DEFAULT_720_60; public static final int DEFAULT_BITRATE = BITRATE_DEFAULT_720_60;
public static final boolean DEFAULT_STRETCH = false; public static final boolean DEFAULT_STRETCH = false;
public static final boolean DEFAULT_SOPS = true;
public static final int FORCE_HARDWARE_DECODER = -1; public static final int FORCE_HARDWARE_DECODER = -1;
public static final int AUTOSELECT_DECODER = 0; public static final int AUTOSELECT_DECODER = 0;
@@ -148,10 +150,12 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
} }
int refreshRate, bitrate; int refreshRate, bitrate;
boolean sops;
width = prefs.getInt(WIDTH_PREF_STRING, DEFAULT_WIDTH); width = prefs.getInt(WIDTH_PREF_STRING, DEFAULT_WIDTH);
height = prefs.getInt(HEIGHT_PREF_STRING, DEFAULT_HEIGHT); height = prefs.getInt(HEIGHT_PREF_STRING, DEFAULT_HEIGHT);
refreshRate = prefs.getInt(REFRESH_RATE_PREF_STRING, DEFAULT_REFRESH_RATE); refreshRate = prefs.getInt(REFRESH_RATE_PREF_STRING, DEFAULT_REFRESH_RATE);
bitrate = prefs.getInt(BITRATE_PREF_STRING, DEFAULT_BITRATE); bitrate = prefs.getInt(BITRATE_PREF_STRING, DEFAULT_BITRATE);
sops = prefs.getBoolean(SOPS_PREF_STRING, DEFAULT_SOPS);
Display display = getWindowManager().getDefaultDisplay(); Display display = getWindowManager().getDefaultDisplay();
display.getSize(screenSize); display.getSize(screenSize);
@@ -160,13 +164,6 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceView); SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceView);
sv.setOnGenericMotionListener(this); sv.setOnGenericMotionListener(this);
sv.setOnTouchListener(this); sv.setOnTouchListener(this);
SurfaceHolder sh = sv.getHolder();
if (stretchToFit) {
// Set the surface to the size of the video
sh.setFixedSize(width, height);
}
// Warn the user if they're on a metered connection // Warn the user if they're on a metered connection
checkDataConnection(); checkDataConnection();
@@ -180,16 +177,22 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
String host = Game.this.getIntent().getStringExtra(EXTRA_HOST); String host = Game.this.getIntent().getStringExtra(EXTRA_HOST);
String app = Game.this.getIntent().getStringExtra(EXTRA_APP); String app = Game.this.getIntent().getStringExtra(EXTRA_APP);
String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID); String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID);
boolean enableLargePackets = !Game.this.getIntent().getBooleanExtra(EXTRA_STREAMING_REMOTE, true);
LimeLog.info("Using large packets? "+enableLargePackets);
// Start the connection // Start the connection
conn = new NvConnection(host, uniqueId, Game.this, conn = new NvConnection(host, uniqueId, Game.this,
new StreamConfiguration(app, width, height, refreshRate, bitrate * 1000, new StreamConfiguration(app, width, height, refreshRate, bitrate * 1000, sops),
enableLargePackets ? 1460 : 1024), PlatformBinding.getCryptoProvider(this)); PlatformBinding.getCryptoProvider(this));
keybTranslator = new KeyboardTranslator(conn); keybTranslator = new KeyboardTranslator(conn);
controllerHandler = new ControllerHandler(conn); controllerHandler = new ControllerHandler(conn);
decoderRenderer = new ConfigurableDecoderRenderer(); decoderRenderer = new ConfigurableDecoderRenderer();
decoderRenderer.initializeWithFlags(drFlags);
SurfaceHolder sh = sv.getHolder();
if (stretchToFit || !decoderRenderer.isHardwareAccelerated()) {
// Set the surface to the size of the video
sh.setFixedSize(width, height);
}
// The connection will be started when the surface gets created // The connection will be started when the surface gets created
sh.addCallback(this); sh.addCallback(this);
@@ -252,7 +255,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
SpinnerDialog.closeDialogs(); SpinnerDialog.closeDialogs(this);
Dialog.closeDialogs(); Dialog.closeDialogs();
displayedFailureDialog = true; displayedFailureDialog = true;
@@ -299,10 +302,6 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
return modifier; return modifier;
} }
private static boolean isSourceFlagSet(int sourcesFlags, int flag) {
return (sourcesFlags & flag) == flag;
}
@Override @Override
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
InputDevice dev = event.getDevice(); InputDevice dev = event.getDevice();
@@ -310,16 +309,15 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
} }
int source = dev.getSources(); // Pass-through virtual navigation keys
boolean handled = false; if ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
if (isSourceFlagSet(source, InputDevice.SOURCE_DPAD) || return super.onKeyDown(keyCode, event);
isSourceFlagSet(source, InputDevice.SOURCE_GAMEPAD) ||
isSourceFlagSet(source, InputDevice.SOURCE_JOYSTICK))
{
handled = controllerHandler.handleButtonDown(keyCode, event);
} }
// Try the controller handler first
boolean handled = controllerHandler.handleButtonDown(keyCode, event);
if (!handled) { if (!handled) {
// Try the keyboard handler
short translated = keybTranslator.translate(event.getKeyCode()); short translated = keybTranslator.translate(event.getKeyCode());
if (translated == 0) { if (translated == 0) {
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
@@ -349,16 +347,15 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
return super.onKeyUp(keyCode, event); return super.onKeyUp(keyCode, event);
} }
int source = dev.getSources(); // Pass-through virtual navigation keys
boolean handled = false; if ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
if (isSourceFlagSet(source, InputDevice.SOURCE_DPAD) || return super.onKeyUp(keyCode, event);
isSourceFlagSet(source, InputDevice.SOURCE_GAMEPAD) ||
isSourceFlagSet(source, InputDevice.SOURCE_JOYSTICK))
{
handled = controllerHandler.handleButtonUp(keyCode, event);
} }
// Try the controller handler first
boolean handled = controllerHandler.handleButtonUp(keyCode, event);
if (!handled) { if (!handled) {
// Try the keyboard handler
short translated = keybTranslator.translate(event.getKeyCode()); short translated = keybTranslator.translate(event.getKeyCode());
if (translated == 0) { if (translated == 0) {
return super.onKeyUp(keyCode, event); return super.onKeyUp(keyCode, event);
@@ -614,7 +611,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
// Resize the surface to match the aspect ratio of the video // Resize the surface to match the aspect ratio of the video
// This must be done after the surface is created. // This must be done after the surface is created.
if (!stretchToFit) { if (!stretchToFit && decoderRenderer.isHardwareAccelerated()) {
resizeSurfaceWithAspectRatio((SurfaceView) findViewById(R.id.surfaceView), width, height); resizeSurfaceWithAspectRatio((SurfaceView) findViewById(R.id.surfaceView), width, height);
} }
+16 -2
View File
@@ -37,7 +37,7 @@ import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.AdapterContextMenuInfo;
public class PcView extends Activity { public class PcView extends Activity {
private Button settingsButton; private Button settingsButton, addComputerButton;
private ListView pcList; private ListView pcList;
private ArrayAdapter<ComputerObject> pcListAdapter; private ArrayAdapter<ComputerObject> pcListAdapter;
private ComputerManagerService.ComputerManagerBinder managerBinder; private ComputerManagerService.ComputerManagerBinder managerBinder;
@@ -85,6 +85,8 @@ public class PcView extends Activity {
// Setup the list view // Setup the list view
settingsButton = (Button)findViewById(R.id.settingsButton); settingsButton = (Button)findViewById(R.id.settingsButton);
addComputerButton = (Button)findViewById(R.id.manuallyAddPc);
pcList = (ListView)findViewById(R.id.pcListView); pcList = (ListView)findViewById(R.id.pcListView);
pcListAdapter = new ArrayAdapter<ComputerObject>(this, R.layout.simplerow, R.id.rowTextView); pcListAdapter = new ArrayAdapter<ComputerObject>(this, R.layout.simplerow, R.id.rowTextView);
pcListAdapter.setNotifyOnChange(false); pcListAdapter.setNotifyOnChange(false);
@@ -119,6 +121,13 @@ public class PcView extends Activity {
startActivity(new Intent(PcView.this, StreamSettings.class)); startActivity(new Intent(PcView.this, StreamSettings.class));
} }
}); });
addComputerButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(PcView.this, AddComputerManually.class);
startActivity(i);
}
});
addListPlaceholder(); addListPlaceholder();
} }
@@ -313,6 +322,11 @@ public class PcView extends Activity {
return; return;
} }
if (computer.macAddress == null) {
Toast.makeText(PcView.this, "Unable to wake PC because GFE didn't send a MAC address", Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(PcView.this, "Waking PC...", Toast.LENGTH_SHORT).show(); Toast.makeText(PcView.this, "Waking PC...", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
@@ -494,7 +508,7 @@ public class PcView extends Activity {
private void addListPlaceholder() { private void addListPlaceholder() {
pcListAdapter.add(new ComputerObject("Discovery is running. No computers found yet. " + pcListAdapter.add(new ComputerObject("Discovery is running. No computers found yet. " +
"If your PC doesn't show up in about 15 seconds, " + "If your PC doesn't show up in about 15 seconds, " +
"make sure your computer is running GFE or add your PC manually on the settings page.", null)); "make sure your computer is running GFE or add your PC manually using the button above.", null));
} }
private void removeListView(ComputerDetails details) { private void removeListView(ComputerDetails details) {
+11 -10
View File
@@ -16,10 +16,10 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
public class StreamSettings extends Activity { public class StreamSettings extends Activity {
private Button advancedSettingsButton, addComputerButton; private Button advancedSettingsButton;
private SharedPreferences prefs; private SharedPreferences prefs;
private RadioButton rbutton720p30, rbutton720p60, rbutton1080p30, rbutton1080p60; private RadioButton rbutton720p30, rbutton720p60, rbutton1080p30, rbutton1080p60;
private CheckBox stretchToFill; private CheckBox stretchToFill, enableSops;
@Override @Override
protected void onStop() { protected void onStop() {
@@ -35,8 +35,8 @@ public class StreamSettings extends Activity {
setContentView(R.layout.activity_stream_settings); setContentView(R.layout.activity_stream_settings);
this.stretchToFill = (CheckBox) findViewById(R.id.stretchToFill); this.stretchToFill = (CheckBox) findViewById(R.id.stretchToFill);
this.enableSops = (CheckBox) findViewById(R.id.enableSops);
this.advancedSettingsButton = (Button) findViewById(R.id.advancedSettingsButton); this.advancedSettingsButton = (Button) findViewById(R.id.advancedSettingsButton);
this.addComputerButton = (Button) findViewById(R.id.manuallyAddPc);
this.rbutton720p30 = (RadioButton) findViewById(R.id.config720p30Selected); this.rbutton720p30 = (RadioButton) findViewById(R.id.config720p30Selected);
this.rbutton720p60 = (RadioButton) findViewById(R.id.config720p60Selected); this.rbutton720p60 = (RadioButton) findViewById(R.id.config720p60Selected);
this.rbutton1080p30 = (RadioButton) findViewById(R.id.config1080p30Selected); this.rbutton1080p30 = (RadioButton) findViewById(R.id.config1080p30Selected);
@@ -48,6 +48,7 @@ public class StreamSettings extends Activity {
boolean fps30 = prefs.getInt(Game.REFRESH_RATE_PREF_STRING, Game.DEFAULT_REFRESH_RATE) == 30; boolean fps30 = prefs.getInt(Game.REFRESH_RATE_PREF_STRING, Game.DEFAULT_REFRESH_RATE) == 30;
stretchToFill.setChecked(prefs.getBoolean(Game.STRETCH_PREF_STRING, Game.DEFAULT_STRETCH)); stretchToFill.setChecked(prefs.getBoolean(Game.STRETCH_PREF_STRING, Game.DEFAULT_STRETCH));
enableSops.setChecked(prefs.getBoolean(Game.SOPS_PREF_STRING, Game.DEFAULT_SOPS));
rbutton720p30.setChecked(false); rbutton720p30.setChecked(false);
rbutton720p60.setChecked(false); rbutton720p60.setChecked(false);
@@ -117,13 +118,6 @@ public class StreamSettings extends Activity {
startActivity(i); startActivity(i);
} }
}); });
addComputerButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(StreamSettings.this, AddComputerManually.class);
startActivity(i);
}
});
stretchToFill.setOnCheckedChangeListener(new OnCheckedChangeListener() { stretchToFill.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, public void onCheckedChanged(CompoundButton buttonView,
@@ -131,5 +125,12 @@ public class StreamSettings extends Activity {
prefs.edit().putBoolean(Game.STRETCH_PREF_STRING, isChecked).commit(); prefs.edit().putBoolean(Game.STRETCH_PREF_STRING, isChecked).commit();
} }
}); });
enableSops.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
prefs.edit().putBoolean(Game.SOPS_PREF_STRING, isChecked).commit();
}
});
} }
} }
@@ -9,6 +9,7 @@ import android.view.MotionEvent;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket; import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.utils.Vector2d;
public class ControllerHandler { public class ControllerHandler {
private short inputMap = 0x0000; private short inputMap = 0x0000;
@@ -37,6 +38,9 @@ public class ControllerHandler {
private static final int EMULATED_SPECIAL_UP_DELAY_MS = 100; private static final int EMULATED_SPECIAL_UP_DELAY_MS = 100;
private static final int EMULATED_SELECT_UP_DELAY_MS = 30; private static final int EMULATED_SELECT_UP_DELAY_MS = 30;
private Vector2d inputVector = new Vector2d();
private Vector2d normalizedInputVector = new Vector2d();
private HashMap<String, ControllerMapping> mappings = new HashMap<String, ControllerMapping>(); private HashMap<String, ControllerMapping> mappings = new HashMap<String, ControllerMapping>();
private NvConnection conn; private NvConnection conn;
@@ -129,29 +133,19 @@ public class ControllerHandler {
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) { if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
InputDevice.MotionRange lsXRange = dev.getMotionRange(mapping.leftStickXAxis); InputDevice.MotionRange lsXRange = dev.getMotionRange(mapping.leftStickXAxis);
InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis); InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis);
if (lsXRange != null) { if (lsXRange != null && lsYRange != null) {
mapping.leftStickXAxisDeadzone = lsXRange.getFlat(); mapping.leftStickDeadzoneRadius = Math.max(lsXRange.getFlat(), lsYRange.getFlat());
}
if (lsYRange != null) {
mapping.leftStickYAxisDeadzone = lsYRange.getFlat();
} }
} }
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) { if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
InputDevice.MotionRange rsXRange = dev.getMotionRange(mapping.rightStickXAxis); InputDevice.MotionRange rsXRange = dev.getMotionRange(mapping.rightStickXAxis);
InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis); InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis);
if (rsXRange != null) { if (rsXRange != null && rsYRange != null) {
mapping.rightStickXAxisDeadzone = rsXRange.getFlat(); mapping.rightStickDeadzoneRadius = Math.max(rsXRange.getFlat(), rsYRange.getFlat());
}
if (rsYRange != null) {
mapping.rightStickYAxisDeadzone = rsYRange.getFlat();
} }
} }
mapping.isDpad = (dev.getSources() & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD;
mapping.isGamepad = (dev.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD ||
(dev.getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK;
return mapping; return mapping;
} }
@@ -181,47 +175,6 @@ public class ControllerHandler {
leftStickX, leftStickY, rightStickX, rightStickY); leftStickX, leftStickY, rightStickX, rightStickY);
} }
private static boolean isEventExpected(ControllerMapping mapping, int keyCode) {
if (mapping.isDpad) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
return true;
}
}
if (mapping.isGamepad) {
switch (keyCode) {
case KeyEvent.KEYCODE_BUTTON_MODE:
case KeyEvent.KEYCODE_BUTTON_START:
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_BUTTON_SELECT:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_BUTTON_B:
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_BUTTON_A:
case KeyEvent.KEYCODE_BUTTON_X:
case KeyEvent.KEYCODE_BUTTON_Y:
case KeyEvent.KEYCODE_BUTTON_L1:
case KeyEvent.KEYCODE_BUTTON_R1:
case KeyEvent.KEYCODE_BUTTON_THUMBL:
case KeyEvent.KEYCODE_BUTTON_THUMBR:
case KeyEvent.KEYCODE_BUTTON_L2:
case KeyEvent.KEYCODE_BUTTON_R2:
return true;
}
}
return false;
}
private static int handleRemapping(ControllerMapping mapping, int keyCode) { private static int handleRemapping(ControllerMapping mapping, int keyCode) {
if (mapping.isDualShock4) { if (mapping.isDualShock4) {
switch (keyCode) { switch (keyCode) {
@@ -277,6 +230,39 @@ public class ControllerHandler {
return keyCode; return keyCode;
} }
private Vector2d handleDeadZone(float x, float y, float deadzoneRadius) {
// Reinitialize our cached Vector2d object
inputVector.initialize(x, y);
if (inputVector.getMagnitude() <= deadzoneRadius) {
// Deadzone -- return the zero vector
return Vector2d.ZERO;
}
else {
// 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);
}
else if (normalizedInputVector.getX() < -1.0f) {
normalizedInputVector.setX(-1.0f);
}
// Bound the Y value to -1.0 to 1.0
if (normalizedInputVector.getY() > 1.0f) {
normalizedInputVector.setY(1.0f);
}
else if (normalizedInputVector.getY() < -1.0f) {
normalizedInputVector.setY(-1.0f);
}
return normalizedInputVector;
}
}
public boolean handleMotionEvent(MotionEvent event) { public boolean handleMotionEvent(MotionEvent event) {
ControllerMapping mapping = getMappingForDevice(event.getDevice()); ControllerMapping mapping = getMappingForDevice(event.getDevice());
if (mapping == null) { if (mapping == null) {
@@ -285,30 +271,20 @@ public class ControllerHandler {
// Handle left stick events outside of the deadzone // Handle left stick events outside of the deadzone
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) { if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
float LS_X = event.getAxisValue(mapping.leftStickXAxis); Vector2d leftStickVector = handleDeadZone(event.getAxisValue(mapping.leftStickXAxis),
float LS_Y = event.getAxisValue(mapping.leftStickYAxis); event.getAxisValue(mapping.leftStickYAxis), mapping.leftStickDeadzoneRadius);
if (LS_X >= -mapping.leftStickXAxisDeadzone && LS_X <= mapping.leftStickXAxisDeadzone) {
LS_X = 0; leftStickX = (short)(leftStickVector.getX() * 0x7FFE);
} leftStickY = (short)(-leftStickVector.getY() * 0x7FFE);
if (LS_Y >= -mapping.leftStickYAxisDeadzone && LS_Y <= mapping.leftStickYAxisDeadzone) {
LS_Y = 0;
}
leftStickX = (short)Math.round(LS_X * 0x7FFF);
leftStickY = (short)Math.round(-LS_Y * 0x7FFF);
} }
// Handle right stick events outside of the deadzone // Handle right stick events outside of the deadzone
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) { if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
float RS_X = event.getAxisValue(mapping.rightStickXAxis); Vector2d rightStickVector = handleDeadZone(event.getAxisValue(mapping.rightStickXAxis),
float RS_Y = event.getAxisValue(mapping.rightStickYAxis); event.getAxisValue(mapping.rightStickYAxis), mapping.rightStickDeadzoneRadius);
if (RS_X >= -mapping.rightStickXAxisDeadzone && RS_X <= mapping.rightStickXAxisDeadzone) {
RS_X = 0; rightStickX = (short)(rightStickVector.getX() * 0x7FFE);
} rightStickY = (short)(-rightStickVector.getY() * 0x7FFE);
if (RS_Y >= -mapping.rightStickYAxisDeadzone && RS_Y <= mapping.rightStickYAxisDeadzone) {
RS_Y = 0;
}
rightStickX = (short)Math.round(RS_X * 0x7FFF);
rightStickY = (short)Math.round(-RS_Y * 0x7FFF);
} }
// Handle controllers with analog triggers // Handle controllers with analog triggers
@@ -321,8 +297,8 @@ public class ControllerHandler {
R2 = (R2 + 1) / 2; R2 = (R2 + 1) / 2;
} }
leftTrigger = (byte)Math.round(L2 * 0xFF); leftTrigger = (byte)(L2 * 0xFF);
rightTrigger = (byte)Math.round(R2 * 0xFF); rightTrigger = (byte)(R2 * 0xFF);
} }
// Hats emulate d-pad events // Hats emulate d-pad events
@@ -362,10 +338,6 @@ public class ControllerHandler {
return true; return true;
} }
if (!isEventExpected(mapping, keyCode)) {
return false;
}
// If the button hasn't been down long enough, sleep for a bit before sending the up event // If the button hasn't been down long enough, sleep for a bit before sending the up event
// This allows "instant" button presses (like OUYA's virtual menu button) to work. This // This allows "instant" button presses (like OUYA's virtual menu button) to work. This
// path should not be triggered during normal usage. // path should not be triggered during normal usage.
@@ -489,10 +461,6 @@ public class ControllerHandler {
return true; return true;
} }
if (!isEventExpected(mapping, keyCode)) {
return false;
}
switch (keyCode) { switch (keyCode) {
case KeyEvent.KEYCODE_BUTTON_MODE: case KeyEvent.KEYCODE_BUTTON_MODE:
inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG; inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
@@ -580,17 +548,13 @@ public class ControllerHandler {
} }
class ControllerMapping { class ControllerMapping {
public int leftStickXAxis = -1; public int leftStickXAxis = -1;
public float leftStickXAxisDeadzone;
public int leftStickYAxis = -1; public int leftStickYAxis = -1;
public float leftStickYAxisDeadzone; public float leftStickDeadzoneRadius;
public int rightStickXAxis = -1; public int rightStickXAxis = -1;
public float rightStickXAxisDeadzone;
public int rightStickYAxis = -1; public int rightStickYAxis = -1;
public float rightStickYAxisDeadzone; public float rightStickDeadzoneRadius;
public int leftTriggerAxis = -1; public int leftTriggerAxis = -1;
public int rightTriggerAxis = -1; public int rightTriggerAxis = -1;
@@ -602,7 +566,5 @@ public class ControllerHandler {
public float hatYDeadzone; public float hatYDeadzone;
public boolean isDualShock4; public boolean isDualShock4;
public boolean isDpad;
public boolean isGamepad;
} }
} }
@@ -16,15 +16,28 @@ public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
@Override @Override
public boolean setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) { public boolean setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
if (decoderRenderer == null) {
throw new IllegalStateException("ConfigurableDecoderRenderer not initialized");
}
return decoderRenderer.setup(width, height, redrawRate, renderTarget, drFlags);
}
public void initializeWithFlags(int drFlags) {
if ((drFlags & VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING) != 0 || if ((drFlags & VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING) != 0 ||
((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 && ((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 &&
MediaCodecDecoderRenderer.findSafeDecoder() != null)) { MediaCodecDecoderRenderer.findSafeDecoder() != null)) {
decoderRenderer = new MediaCodecDecoderRenderer(); decoderRenderer = new MediaCodecDecoderRenderer();
} }
else { else {
decoderRenderer = new AndroidCpuDecoderRenderer(); decoderRenderer = new AndroidCpuDecoderRenderer();
} }
return decoderRenderer.setup(width, height, redrawRate, renderTarget, drFlags); }
public boolean isHardwareAccelerated() {
if (decoderRenderer == null) {
throw new IllegalStateException("ConfigurableDecoderRenderer not initialized");
}
return (decoderRenderer instanceof MediaCodecDecoderRenderer);
} }
@Override @Override
@@ -36,7 +36,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
private final static byte[] BITSTREAM_RESTRICTIONS = new byte[] {(byte) 0xF1, (byte) 0x83, 0x2A, 0x00}; private final static byte[] BITSTREAM_RESTRICTIONS = new byte[] {(byte) 0xF1, (byte) 0x83, 0x2A, 0x00};
public static final List<String> blacklistedDecoderPrefixes; public static final List<String> blacklistedDecoderPrefixes;
public static final List<String> spsFixupBitsreamFixupDecoderPrefixes; public static final List<String> spsFixupBitstreamFixupDecoderPrefixes;
public static final List<String> spsFixupNumRefFixupDecoderPrefixes; public static final List<String> spsFixupNumRefFixupDecoderPrefixes;
static { static {
@@ -46,14 +46,17 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
} }
static { static {
spsFixupBitsreamFixupDecoderPrefixes = new LinkedList<String>(); spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<String>();
spsFixupBitsreamFixupDecoderPrefixes.add("omx.nvidia"); spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.sec");
spsFixupNumRefFixupDecoderPrefixes = new LinkedList<String>(); spsFixupNumRefFixupDecoderPrefixes = new LinkedList<String>();
spsFixupNumRefFixupDecoderPrefixes.add("omx.TI"); spsFixupNumRefFixupDecoderPrefixes.add("omx.TI");
spsFixupNumRefFixupDecoderPrefixes.add("omx.qcom"); spsFixupNumRefFixupDecoderPrefixes.add("omx.qcom");
spsFixupNumRefFixupDecoderPrefixes.add("omx.sec");
} }
private static boolean isDecoderInList(List<String> decoderList, String decoderName) { private static boolean isDecoderInList(List<String> decoderList, String decoderName) {
for (String badPrefix : decoderList) { for (String badPrefix : decoderList) {
if (decoderName.length() >= badPrefix.length()) { if (decoderName.length() >= badPrefix.length()) {
@@ -136,7 +139,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
MediaCodecInfo safeDecoder = findSafeDecoder(); MediaCodecInfo safeDecoder = findSafeDecoder();
if (safeDecoder != null) { if (safeDecoder != null) {
videoDecoder = MediaCodec.createByCodecName(safeDecoder.getName()); videoDecoder = MediaCodec.createByCodecName(safeDecoder.getName());
needsSpsBitstreamFixup = isDecoderInList(spsFixupBitsreamFixupDecoderPrefixes, safeDecoder.getName()); needsSpsBitstreamFixup = isDecoderInList(spsFixupBitstreamFixupDecoderPrefixes, safeDecoder.getName());
needsSpsNumRefFixup = isDecoderInList(spsFixupNumRefFixupDecoderPrefixes, safeDecoder.getName()); needsSpsNumRefFixup = isDecoderInList(spsFixupNumRefFixupDecoderPrefixes, safeDecoder.getName());
if (needsSpsBitstreamFixup) { if (needsSpsBitstreamFixup) {
LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS bitstream restrictions fixup"); LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS bitstream restrictions fixup");
@@ -177,8 +180,14 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
{ {
du = depacketizer.pollNextDecodeUnit(); du = depacketizer.pollNextDecodeUnit();
if (du != null) { if (du != null) {
submitDecodeUnit(du); if (!submitDecodeUnit(du)) {
depacketizer.freeDecodeUnit(du); // Thread was interrupted
depacketizer.freeDecodeUnit(du);
return;
}
else {
depacketizer.freeDecodeUnit(du);
}
} }
int outIndex = videoDecoder.dequeueOutputBuffer(info, 0); int outIndex = videoDecoder.dequeueOutputBuffer(info, 0);
@@ -250,105 +259,111 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
} }
private boolean submitDecodeUnit(DecodeUnit decodeUnit) { private boolean submitDecodeUnit(DecodeUnit decodeUnit) {
int inputIndex = videoDecoder.dequeueInputBuffer(-1); int inputIndex;
if (inputIndex >= 0)
{ do {
ByteBuffer buf = videoDecoderInputBuffers[inputIndex]; if (Thread.interrupted()) {
return false;
long currentTime = System.currentTimeMillis();
long delta = currentTime-decodeUnit.getReceiveTimestamp();
if (delta >= 0 && delta < 300) {
totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp();
totalFrames++;
} }
// Clear old input data inputIndex = videoDecoder.dequeueInputBuffer(100000);
buf.clear(); } while (inputIndex < 0);
int codecFlags = 0; ByteBuffer buf = videoDecoderInputBuffers[inputIndex];
int decodeUnitFlags = decodeUnit.getFlags();
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0 &&
(needsSpsBitstreamFixup || needsSpsNumRefFixup)) {
ByteBufferDescriptor header = decodeUnit.getBufferList().get(0);
if (header.data[header.offset+4] == 0x67) {
byte last = header.data[header.length+header.offset-1];
// TI OMAP4 requires a reference frame count of 1 to decode successfully long currentTime = System.currentTimeMillis();
if (needsSpsNumRefFixup) { long delta = currentTime-decodeUnit.getReceiveTimestamp();
LimeLog.info("Fixing up num ref frames"); if (delta >= 0 && delta < 300) {
this.replace(header, 80, 9, new byte[] {0x40}, 3); totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp();
} totalFrames++;
}
// Clear old input data
buf.clear();
int codecFlags = 0;
int decodeUnitFlags = decodeUnit.getFlags();
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0 &&
(needsSpsBitstreamFixup || needsSpsNumRefFixup)) {
ByteBufferDescriptor header = decodeUnit.getBufferList().get(0);
if (header.data[header.offset+4] == 0x67) {
byte last = header.data[header.length+header.offset-1];
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag // TI OMAP4 requires a reference frame count of 1 to decode successfully
// or max_dec_frame_buffering which increases decoding latency on Tegra. if (needsSpsNumRefFixup) {
// We manually modify the SPS here to speed-up decoding if the decoder was flagged as needing it. LimeLog.info("Fixing up num ref frames");
int spsLength; this.replace(header, 80, 9, new byte[] {0x40}, 3);
if (needsSpsBitstreamFixup) { }
if (!needsSpsNumRefFixup) {
switch (header.length) { // The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
case 26: // or max_dec_frame_buffering which increases decoding latency on Tegra.
LimeLog.info("Adding bitstream restrictions to SPS (26)"); // We manually modify the SPS here to speed-up decoding if the decoder was flagged as needing it.
buf.put(header.data, header.offset, 24); int spsLength;
buf.put((byte) 0x11); if (needsSpsBitstreamFixup) {
buf.put((byte) 0xe3); if (!needsSpsNumRefFixup) {
buf.put((byte) 0x06); switch (header.length) {
buf.put((byte) 0x50); case 26:
spsLength = header.length + 2; LimeLog.info("Adding bitstream restrictions to SPS (26)");
break; buf.put(header.data, header.offset, 24);
case 27: buf.put((byte) 0x11);
LimeLog.info("Adding bitstream restrictions to SPS (27)"); buf.put((byte) 0xe3);
buf.put(header.data, header.offset, 25); buf.put((byte) 0x06);
buf.put((byte) 0x04); buf.put((byte) 0x50);
buf.put((byte) 0x78); spsLength = header.length + 2;
buf.put((byte) 0xc1); break;
buf.put((byte) 0x94); case 27:
spsLength = header.length + 2; LimeLog.info("Adding bitstream restrictions to SPS (27)");
break; buf.put(header.data, header.offset, 25);
default: buf.put((byte) 0x04);
LimeLog.warning("Unknown SPS of length "+header.length); buf.put((byte) 0x78);
buf.put(header.data, header.offset, header.length); buf.put((byte) 0xc1);
spsLength = header.length; buf.put((byte) 0x94);
break; spsLength = header.length + 2;
} break;
} default:
else { LimeLog.warning("Unknown SPS of length "+header.length);
// Set bitstream restrictions to only buffer single frame
// (starts 9 bits before stop bit and 6 bits earlier because of the shortening above)
this.replace(header, header.length*8+Integer.numberOfLeadingZeros(last & - last)%8-9-6, 2, BITSTREAM_RESTRICTIONS, 3*8);
buf.put(header.data, header.offset, header.length); buf.put(header.data, header.offset, header.length);
spsLength = header.length; spsLength = header.length;
break;
} }
} }
else { else {
// Set bitstream restrictions to only buffer single frame
// (starts 9 bits before stop bit and 6 bits earlier because of the shortening above)
this.replace(header, header.length*8+Integer.numberOfLeadingZeros(last & - last)%8-9-6, 2, BITSTREAM_RESTRICTIONS, 3*8);
buf.put(header.data, header.offset, header.length); buf.put(header.data, header.offset, header.length);
spsLength = header.length; spsLength = header.length;
} }
videoDecoder.queueInputBuffer(inputIndex,
0, spsLength,
currentTime * 1000, codecFlags);
return true;
} }
} else {
buf.put(header.data, header.offset, header.length);
spsLength = header.length;
}
// Copy data from our buffer list into the input buffer videoDecoder.queueInputBuffer(inputIndex,
for (ByteBufferDescriptor desc : decodeUnit.getBufferList()) 0, spsLength,
{ currentTime * 1000, codecFlags);
buf.put(desc.data, desc.offset, desc.length); return true;
} }
videoDecoder.queueInputBuffer(inputIndex,
0, decodeUnit.getDataLength(),
currentTime * 1000, codecFlags);
} }
// Copy data from our buffer list into the input buffer
for (ByteBufferDescriptor desc : decodeUnit.getBufferList())
{
buf.put(desc.data, desc.offset, desc.length);
}
videoDecoder.queueInputBuffer(inputIndex,
0, decodeUnit.getDataLength(),
currentTime * 1000, codecFlags);
return true; return true;
} }
@@ -109,6 +109,8 @@ public class ComputerDatabaseManager {
computerList.add(details); computerList.add(details);
} }
c.close();
return computerList; return computerList;
} }
@@ -117,6 +119,7 @@ public class ComputerDatabaseManager {
ComputerDetails details = new ComputerDetails(); ComputerDetails details = new ComputerDetails();
if (!c.moveToFirst()) { if (!c.moveToFirst()) {
// No matching computer // No matching computer
c.close();
return null; return null;
} }
@@ -146,6 +149,8 @@ public class ComputerDatabaseManager {
details.macAddress = c.getString(4); details.macAddress = c.getString(4);
c.close();
// If a field is corrupt or missing, delete the database entry // If a field is corrupt or missing, delete the database entry
if (details.uuid == null || details.localIp == null || details.remoteIp == null || if (details.uuid == null || details.localIp == null || details.remoteIp == null ||
details.macAddress == null) { details.macAddress == null) {
@@ -12,6 +12,7 @@ import java.util.TimerTask;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.limelight.LimeLog; import com.limelight.LimeLog;
import com.limelight.binding.PlatformBinding; import com.limelight.binding.PlatformBinding;
@@ -39,6 +40,8 @@ public class ComputerManagerService extends Service {
private ComputerManagerBinder binder = new ComputerManagerBinder(); private ComputerManagerBinder binder = new ComputerManagerBinder();
private ComputerDatabaseManager dbManager; private ComputerDatabaseManager dbManager;
private AtomicInteger dbRefCount = new AtomicInteger(0);
private IdentityManager idManager; private IdentityManager idManager;
private ThreadPoolExecutor pollingPool; private ThreadPoolExecutor pollingPool;
private Timer pollingTimer; private Timer pollingTimer;
@@ -173,15 +176,41 @@ public class ComputerManagerService extends Service {
} }
public void removeComputer(String name) { public void removeComputer(String name) {
if (!getLocalDatabaseReference()) {
return;
}
// Remove it from the database // Remove it from the database
dbManager.deleteComputer(name); dbManager.deleteComputer(name);
releaseLocalDatabaseReference();
}
private boolean getLocalDatabaseReference() {
if (dbRefCount.get() == 0) {
return false;
}
dbRefCount.incrementAndGet();
return true;
}
private void releaseLocalDatabaseReference() {
if (dbRefCount.decrementAndGet() == 0) {
dbManager.close();
}
} }
private TimerTask getTimerTask() { private TimerTask getTimerTask() {
return new TimerTask() { return new TimerTask() {
@Override @Override
public void run() { public void run() {
if (!getLocalDatabaseReference()) {
return;
}
List<ComputerDetails> computerList = dbManager.getAllComputers(); List<ComputerDetails> computerList = dbManager.getAllComputers();
releaseLocalDatabaseReference();
for (ComputerDetails computer : computerList) { for (ComputerDetails computer : computerList) {
pollingPool.execute(getPollingRunnable(computer)); pollingPool.execute(getPollingRunnable(computer));
} }
@@ -356,6 +385,10 @@ public class ComputerManagerService extends Service {
public void run() { public void run() {
boolean newPc = (details.name == null); boolean newPc = (details.name == null);
if (!getLocalDatabaseReference()) {
return;
}
// Poll the machine // Poll the machine
if (!doPollMachine(details)) { if (!doPollMachine(details)) {
details.state = ComputerDetails.State.OFFLINE; details.state = ComputerDetails.State.OFFLINE;
@@ -369,6 +402,7 @@ public class ComputerManagerService extends Service {
// removed after this was issued // removed after this was issued
if (dbManager.getComputerByName(details.name) == null) { if (dbManager.getComputerByName(details.name) == null) {
// It's gone // It's gone
releaseLocalDatabaseReference();
return; return;
} }
} }
@@ -376,10 +410,12 @@ public class ComputerManagerService extends Service {
dbManager.updateComputer(details); dbManager.updateComputer(details);
} }
// Update anyone listening // Don't call the listener if this is a failed lookup of a new PC
if (listener != null) { if ((!newPc || details.state == ComputerDetails.State.ONLINE) && listener != null) {
listener.notifyComputerUpdated(details); listener.notifyComputerUpdated(details);
} }
releaseLocalDatabaseReference();
} }
}; };
} }
@@ -391,14 +427,16 @@ public class ComputerManagerService extends Service {
discoveryServiceConnection, Service.BIND_AUTO_CREATE); discoveryServiceConnection, Service.BIND_AUTO_CREATE);
// Create the thread pool for updating computer state // Create the thread pool for updating computer state
pollingPool = new ThreadPoolExecutor(1, MAX_CONCURRENT_REQUESTS, Long.MAX_VALUE, TimeUnit.DAYS, pollingPool = new ThreadPoolExecutor(MAX_CONCURRENT_REQUESTS, MAX_CONCURRENT_REQUESTS,
new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy()); Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>(),
new ThreadPoolExecutor.DiscardPolicy());
// Lookup or generate this device's UID // Lookup or generate this device's UID
idManager = new IdentityManager(this); idManager = new IdentityManager(this);
// Initialize the DB // Initialize the DB
dbManager = new ComputerDatabaseManager(this); dbManager = new ComputerDatabaseManager(this);
dbRefCount.set(1);
} }
@Override @Override
@@ -410,12 +448,11 @@ public class ComputerManagerService extends Service {
// Stop the thread pool // Stop the thread pool
pollingPool.shutdownNow(); pollingPool.shutdownNow();
try {
pollingPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {}
// Close the DB // FIXME: Should await termination here but we have timeout issues in HttpURLConnection
dbManager.close();
// Remove the initial DB reference
releaseLocalDatabaseReference();
} }
@Override @Override
+15 -10
View File
@@ -1,6 +1,7 @@
package com.limelight.utils; package com.limelight.utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
@@ -31,16 +32,19 @@ public class SpinnerDialog implements Runnable,OnCancelListener {
return spinner; return spinner;
} }
public static void closeDialogs() public static void closeDialogs(Activity activity)
{ {
synchronized (rundownDialogs) { synchronized (rundownDialogs) {
for (SpinnerDialog d : rundownDialogs) { Iterator<SpinnerDialog> i = rundownDialogs.iterator();
if (d.progress.isShowing()) { while (i.hasNext()) {
d.progress.dismiss(); SpinnerDialog dialog = i.next();
if (dialog.activity == activity) {
i.remove();
if (dialog.progress.isShowing()) {
dialog.progress.dismiss();
}
} }
} }
rundownDialogs.clear();
} }
} }
@@ -62,13 +66,14 @@ public class SpinnerDialog implements Runnable,OnCancelListener {
@Override @Override
public void run() { public void run() {
// If we're dying, don't bother doing anything
if (activity.isFinishing()) {
return;
}
if (progress == null) if (progress == null)
{ {
// If we're dying, don't bother creating a dialog
if (activity.isFinishing())
return;
progress = new ProgressDialog(activity); progress = new ProgressDialog(activity);
progress.setTitle(title); progress.setTitle(title);
+47
View File
@@ -0,0 +1,47 @@
package com.limelight.utils;
public class Vector2d {
private float x;
private float y;
private double magnitude;
public static final Vector2d ZERO = new Vector2d();
public Vector2d() {
initialize(0, 0);
}
public void initialize(float x, float y) {
this.x = x;
this.y = y;
this.magnitude = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}
public double getMagnitude() {
return magnitude;
}
public void getNormalized(Vector2d vector) {
vector.initialize((float)(x / magnitude), (float)(y / magnitude));
}
public void scalarMultiply(double factor) {
initialize((float)(x * factor), (float)(y * factor));
}
public void setX(float x) {
initialize(x, this.y);
}
public void setY(float y) {
initialize(this.x, y);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}