Compare commits

..

14 Commits

Author SHA1 Message Date
Cameron Gutman 5beffed9b9 Update version to 2.5.0.3 2014-07-12 16:19:22 -07:00
Cameron Gutman 532ec6f8f3 Update common 2014-07-12 16:16:35 -07:00
Cameron Gutman a46fb7ba36 Fix small race potential 2014-07-12 16:16:18 -07:00
Cameron Gutman bc2f2de6c2 Update common 2014-07-12 14:07:41 -07:00
Cameron Gutman efec35afa6 Update version to 2.5.0.2 2014-07-12 14:00:52 -07:00
Cameron Gutman 993711ccfe Don't report decoding time for software decoder because it generally doesn't capture the information we want due to CPU overhead 2014-07-12 13:46:39 -07:00
Cameron Gutman d1ef912984 Remove the remaining allocations in the AV paths 2014-07-12 13:38:28 -07:00
Cameron Gutman d2f8ee8b81 Add timekeeping stats to CPU decoder 2014-07-10 18:59:05 -07:00
Cameron Gutman 91e68c0580 Include rendering time in the total decoding time 2014-07-10 18:47:30 -07:00
Cameron Gutman a591dcec48 Disable write-ahead logging on the computer database 2014-07-10 18:42:42 -07:00
Cameron Gutman 1898fcd741 Prevent crashing if the user exits the AddComputerManually activity before the job completes 2014-07-06 15:55:39 -07:00
Cameron Gutman 8bc8f14c64 Protect from accessing the ComputerManagerService before initialization has completed 2014-07-06 12:49:57 -07:00
Cameron Gutman 09cf5d23ea Small update to fix SecurityException on some older devices 2014-07-05 21:31:21 -07:00
Cameron Gutman bec5cfe4a6 Update readme 2014-07-05 13:40:40 -07:00
9 changed files with 164 additions and 70 deletions
+3 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.limelight"
android:versionCode="20"
android:versionName="2.5" >
android:versionCode="23"
android:versionName="2.5.0.3" >
<uses-sdk
android:minSdkVersion="16"
@@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.wifi" android:required="false" />
+8 -10
View File
@@ -3,43 +3,41 @@
Limelight is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield.
We reverse engineered the Shield streaming software, and created a version that can be run on any Android device.
Limelight will allow you to stream your full collection of Steam games from your Windows PC to your Android device,
Limelight will allow you to stream your full collection of games from your Windows PC to your Android device,
in your own home, or over the internet.
[Limelight-pc](https://github.com/limelight-stream/limelight-pc) is also currently in development for Windows, OS X and Linux. Versions for [iOS](https://github.com/limelight-stream/limelight-ios) and [Windows Phone](https://github.com/limelight-stream/limelight-wp) are also in development.
##Features
* Streams Steam and all of your games from your PC to your Android device
* Streams any of your games from your PC to your Android device
* Full gamepad support for MOGA, Xbox 360, PS3, OUYA, and Shield
* Automatically finds GameStream-compatible PCs on your network
##Features in development
* Use mDNS to scan for compatible GeForce Experience (GFE) machines on the network
* Choose from the list of available games instead of just launching Steam
* Keyboard input
##Installation
* Download and install Limelight for Android from
[XDA](http://forum.xda-developers.com/showthread.php?t=2505510)
[Google Play](https://play.google.com/store/apps/details?id=com.limelight)
* Download [GeForce Experience](http://www.geforce.com/geforce-experience) and install on your Windows PC
##Requirements
* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700 series GPU
* [GameStream compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700 series GPU
* Android device running 4.1 (Jelly Bean) or higher
* High-end wireless router (802.11n dual-band recommended)
* Exynos/Snapdragon SoC __OR__ Quad-Core 1.4 GHz Cortex-A9 or higher (Tegra 3)
##Usage
* Turn on Shield Streaming in the GFE settings
* Turn on GameStream in the GFE settings
* If you are connecting from outside the same network, turn on internet
streaming
* In Limelight, enter your PC's IP or Hostname and click "Pair"
* When on the same network as your PC, open Limelight and tap on your PC in the list
* Accept the pairing confirmation on your PC
* In Limelight, click "Start Streaming"
* Tap your PC again to view the list of apps to stream
* Play games!
##Contribute
Binary file not shown.
+88 -40
View File
@@ -2,6 +2,7 @@ package com.limelight;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.LinkedBlockingQueue;
import com.limelight.computers.ComputerManagerService;
import com.limelight.utils.Dialog;
@@ -22,51 +23,86 @@ import android.widget.Toast;
public class AddComputerManually extends Activity {
private Button addPcButton;
private TextView hostText;
private ComputerManagerService.ComputerManagerBinder managerBinder;
private LinkedBlockingQueue<String> computersToAdd = new LinkedBlockingQueue<String>();
private Thread addThread;
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, final IBinder binder) {
new Thread() {
@Override
public void run() {
String msg;
boolean finish = false;
try {
InetAddress addr = InetAddress.getByName(hostText.getText().toString());
if (!((ComputerManagerService.ComputerManagerBinder)binder).addComputerBlocking(addr)){
msg = "Unable to connect to the specified computer. Make sure the required ports are allowed through the firewall.";
}
else {
msg = "Successfully added computer";
finish = true;
}
} catch (UnknownHostException e) {
msg = "Unable to resolve PC address. Make sure you didn't make a typo in the address.";
}
final boolean toastFinish = finish;
final String toastMsg = msg;
AddComputerManually.this.runOnUiThread(new Runnable() {
@Override
public void run() {
// Unbind from this service
unbindService(AddComputerManually.this.serviceConnection);
Toast.makeText(AddComputerManually.this, toastMsg, Toast.LENGTH_LONG).show();
if (toastFinish) {
// Close the activity
AddComputerManually.this.finish();
}
}
});
}
}.start();
managerBinder = ((ComputerManagerService.ComputerManagerBinder)binder);
startAddThread();
}
public void onServiceDisconnected(ComponentName className) {
joinAddThread();
managerBinder = null;
}
};
private void doAddPc(String host) {
String msg;
boolean finish = false;
try {
InetAddress addr = InetAddress.getByName(host);
if (!managerBinder.addComputerBlocking(addr)){
msg = "Unable to connect to the specified computer. Make sure the required ports are allowed through the firewall.";
}
else {
msg = "Successfully added computer";
finish = true;
}
} catch (UnknownHostException e) {
msg = "Unable to resolve PC address. Make sure you didn't make a typo in the address.";
}
final boolean toastFinish = finish;
final String toastMsg = msg;
AddComputerManually.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(AddComputerManually.this, toastMsg, Toast.LENGTH_LONG).show();
if (toastFinish && !isFinishing()) {
// Close the activity
AddComputerManually.this.finish();
}
}
});
}
private void startAddThread() {
addThread = new Thread() {
@Override
public void run() {
while (!isInterrupted()) {
String computer;
try {
computer = computersToAdd.take();
} catch (InterruptedException e) {
return;
}
doAddPc(computer);
}
}
};
addThread.setName("UI - AddComputerManually");
addThread.start();
}
private void joinAddThread() {
if (addThread != null) {
addThread.interrupt();
try {
addThread.join();
} catch (InterruptedException e) {}
addThread = null;
}
}
@Override
protected void onStop() {
super.onStop();
@@ -74,6 +110,16 @@ public class AddComputerManually extends Activity {
Dialog.closeDialogs();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (managerBinder != null) {
joinAddThread();
unbindService(serviceConnection);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -82,6 +128,10 @@ public class AddComputerManually extends Activity {
this.addPcButton = (Button) findViewById(R.id.addPc);
this.hostText = (TextView) findViewById(R.id.hostTextView);
// Bind to the ComputerManager service
bindService(new Intent(AddComputerManually.this,
ComputerManagerService.class), serviceConnection, Service.BIND_AUTO_CREATE);
addPcButton.setOnClickListener(new OnClickListener() {
@Override
@@ -92,9 +142,7 @@ public class AddComputerManually extends Activity {
}
Toast.makeText(AddComputerManually.this, "Adding PC...", Toast.LENGTH_SHORT).show();
// Bind to the service which will try to add the PC
bindService(new Intent(AddComputerManually.this, ComputerManagerService.class), serviceConnection, Service.BIND_AUTO_CREATE);
computersToAdd.add(hostText.getText().toString());
}
});
}
+31 -6
View File
@@ -148,9 +148,16 @@ public class PcView extends Activity {
}
private void stopComputerUpdates() {
freezeUpdates = true;
managerBinder.stopPolling();
runningPolling = false;
if (managerBinder != null) {
if (!runningPolling) {
return;
}
freezeUpdates = true;
managerBinder.stopPolling();
runningPolling = false;
}
}
@Override
@@ -229,6 +236,11 @@ public class PcView extends Activity {
"You must close the game before pairing.", Toast.LENGTH_LONG).show();
return;
}
if (managerBinder == null) {
Toast.makeText(PcView.this, "The ComputerManager service is not running. " +
"Please wait a few seconds or restart the app.", Toast.LENGTH_LONG).show();
return;
}
Toast.makeText(PcView.this, "Pairing...", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@@ -330,6 +342,11 @@ public class PcView extends Activity {
Toast.makeText(PcView.this, "Computer is offline", Toast.LENGTH_SHORT).show();
return;
}
if (managerBinder == null) {
Toast.makeText(PcView.this, "The ComputerManager service is not running. " +
"Please wait a few seconds or restart the app.", Toast.LENGTH_LONG).show();
return;
}
Toast.makeText(PcView.this, "Unpairing...", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@@ -387,6 +404,11 @@ public class PcView extends Activity {
Toast.makeText(PcView.this, "Computer is offline", Toast.LENGTH_SHORT).show();
return;
}
if (managerBinder == null) {
Toast.makeText(PcView.this, "The ComputerManager service is not running. " +
"Please wait a few seconds or restart the app.", Toast.LENGTH_LONG).show();
return;
}
Intent i = new Intent(this, AppView.class);
i.putExtra(AppView.NAME_EXTRA, computer.name);
@@ -422,9 +444,12 @@ public class PcView extends Activity {
return true;
case DELETE_ID:
if (managerBinder != null) {
managerBinder.removeComputer(computer.details.name);
}
if (managerBinder == null) {
Toast.makeText(PcView.this, "The ComputerManager service is not running. " +
"Please wait a few seconds or restart the app.", Toast.LENGTH_LONG).show();
return true;
}
managerBinder.removeComputer(computer.details.name);
removeListView(computer.details);
return true;
@@ -31,6 +31,9 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
private static final int MED_PERF = 2;
private static final int HIGH_PERF = 3;
private int totalFrames;
private long totalTimeMs;
private int cpuCount = Runtime.getRuntime().availableProcessors();
private int findOptimalPerformanceLevel() {
@@ -155,6 +158,7 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
du = depacketizer.pollNextDecodeUnit();
if (du != null) {
submitDecodeUnit(du);
depacketizer.freeDecodeUnit(du);
}
long diff = nextFrameTime - System.currentTimeMillis();
@@ -215,7 +219,19 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
}
}
return (AvcDecoder.decode(data, 0, decodeUnit.getDataLength()) == 0);
boolean success = (AvcDecoder.decode(data, 0, decodeUnit.getDataLength()) == 0);
if (success) {
long timeAfterDecode = System.currentTimeMillis();
// Add delta time to the totals (excluding probable outliers)
long delta = timeAfterDecode - decodeUnit.getReceiveTimestamp();
if (delta >= 0 && delta < 300) {
totalTimeMs += delta;
totalFrames++;
}
}
return success;
}
@Override
@@ -230,6 +246,9 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
@Override
public int getAverageEndToEndLatency() {
return 0;
if (totalFrames == 0) {
return 0;
}
return (int)(totalTimeMs / totalFrames);
}
}
@@ -177,27 +177,30 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
du = depacketizer.pollNextDecodeUnit();
if (du != null) {
submitDecodeUnit(du);
depacketizer.freeDecodeUnit(du);
}
int outIndex = videoDecoder.dequeueOutputBuffer(info, 0);
if (outIndex >= 0) {
long presentationTimeUs = info.presentationTimeUs;
int lastIndex = outIndex;
// Add delta time to the totals (excluding probable outliers)
long delta = System.currentTimeMillis()-(info.presentationTimeUs/1000);
if (delta > 5 && delta < 300) {
decoderTimeMs += delta;
totalTimeMs += delta;
}
// Get the last output buffer in the queue
while ((outIndex = videoDecoder.dequeueOutputBuffer(info, 0)) >= 0) {
videoDecoder.releaseOutputBuffer(lastIndex, false);
lastIndex = outIndex;
presentationTimeUs = info.presentationTimeUs;
}
// Render the last buffer
videoDecoder.releaseOutputBuffer(lastIndex, true);
// Add delta time to the totals (excluding probable outliers)
long delta = System.currentTimeMillis()-(presentationTimeUs/1000);
if (delta > 5 && delta < 300) {
decoderTimeMs += delta;
totalTimeMs += delta;
}
} else {
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
@@ -35,7 +35,6 @@ public class ComputerDatabaseManager {
c.deleteDatabase(COMPUTER_DB_NAME);
computerDb = c.openOrCreateDatabase(COMPUTER_DB_NAME, 0, null);
}
computerDb.enableWriteAheadLogging();
initializeDb();
}
@@ -48,12 +48,13 @@ public class ComputerManagerService extends Service {
private ServiceConnection discoveryServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
synchronized (discoveryServiceConnection) {
discoveryBinder = ((DiscoveryService.DiscoveryBinder)binder);
DiscoveryService.DiscoveryBinder privateBinder = ((DiscoveryService.DiscoveryBinder)binder);
// Set us as the event listener
discoveryBinder.setListener(createDiscoveryListener());
privateBinder.setListener(createDiscoveryListener());
// Signal a possible waiter that we're all setup
discoveryBinder = privateBinder;
discoveryServiceConnection.notifyAll();
}
}