Compare commits

..
11 Commits
25 changed files with 99 additions and 65 deletions

No files matched your search

+8 -10
View File
@@ -1,14 +1,14 @@
#Limelight #Moonlight
Limelight is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield. Moonlight 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. 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 games from your Windows PC to your Android device, Moonlight will allow you to stream your full collection of games from your Windows PC to your Android device,
whether in your own home or over the internet. whether 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 and Windows Phone](https://github.com/limelight-stream/limelight-windows) are also in development. [Moonlight-pc](https://github.com/moonlight-stream/moonlight-pc) is also currently in development for Windows, OS X and Linux. Versions for [iOS](https://github.com/moonlight-stream/moonlight-ios) and [Windows and Windows Phone](https://github.com/moonlight-stream/moonlight-windows) are also in development.
Check our [wiki](https://github.com/limelight-stream/limelight-android/wiki) for more detailed information or a troubleshooting guide. Check our [wiki](https://github.com/moonlight-stream/moonlight-android/wiki) for more detailed information or a troubleshooting guide.
##Features ##Features
@@ -18,7 +18,7 @@ Check our [wiki](https://github.com/limelight-stream/limelight-android/wiki) for
##Installation ##Installation
* Download and install Limelight for Android from * Download and install Moonlight for Android from
[Google Play](https://play.google.com/store/apps/details?id=com.limelight) [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 * Download [GeForce Experience](http://www.geforce.com/geforce-experience) and install on your Windows PC
@@ -33,7 +33,7 @@ Check our [wiki](https://github.com/limelight-stream/limelight-android/wiki) for
* Turn on GameStream in the GFE settings * Turn on GameStream in the GFE settings
* If you are connecting from outside the same network, turn on internet * If you are connecting from outside the same network, turn on internet
streaming streaming
* When on the same network as your PC, open Limelight and tap on your PC in the list * When on the same network as your PC, open Moonlight and tap on your PC in the list
* Accept the pairing confirmation on your PC * Accept the pairing confirmation on your PC
* Tap your PC again to view the list of apps to stream * Tap your PC again to view the list of apps to stream
* Play games! * Play games!
@@ -46,8 +46,6 @@ This project is being actively developed at [XDA Developers](http://forum.xda-de
2. Write code 2. Write code
3. Send Pull Requests 3. Send Pull Requests
Check out our [website](http://limelight-stream.com) for project links and information.
##Authors ##Authors
* [Cameron Gutman](https://github.com/cgutman) * [Cameron Gutman](https://github.com/cgutman)
@@ -55,5 +53,5 @@ Check out our [website](http://limelight-stream.com) for project links and infor
* [Aaron Neyer](https://github.com/Aaronneyer) * [Aaron Neyer](https://github.com/Aaronneyer)
* [Andrew Hennessy](https://github.com/yetanothername) * [Andrew Hennessy](https://github.com/yetanothername)
Limelight is the work of students at [Case Western](http://case.edu) and was Moonlight is the work of students at [Case Western](http://case.edu) and was
started as a project at [MHacks](http://mhacks.org). started as a project at [MHacks](http://mhacks.org).
+1 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="limelight-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> <module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="moonlight-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle"> <facet type="android-gradle" name="Android-Gradle">
<configuration> <configuration>
+2 -2
View File
@@ -11,8 +11,8 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 22 targetSdkVersion 22
versionName "3.1.3" versionName "3.1.5"
versionCode = 58 versionCode = 60
} }
productFlavors { productFlavors {
Binary file not shown.
@@ -97,18 +97,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
return range; return range;
} }
private short assignNewControllerNumber() {
for (short i = 0; i < 4; i++) {
if ((currentControllers & (1 << i)) == 0) {
// Found an unused controller value
currentControllers |= (1 << i);
return i;
}
}
return 0;
}
@Override @Override
public void onInputDeviceAdded(int deviceId) { public void onInputDeviceAdded(int deviceId) {
// Nothing happening here yet // Nothing happening here yet
@@ -119,7 +107,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
for (Map.Entry<String, ControllerContext> device : contexts.entrySet()) { for (Map.Entry<String, ControllerContext> device : contexts.entrySet()) {
if (device.getValue().id == deviceId) { if (device.getValue().id == deviceId) {
LimeLog.info("Removed controller: "+device.getValue().name); LimeLog.info("Removed controller: "+device.getValue().name);
releaseControllerNumber(device.getValue().controllerNumber); releaseControllerNumber(device.getValue());
contexts.remove(device.getKey()); contexts.remove(device.getKey());
return; return;
} }
@@ -133,9 +121,47 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
onInputDeviceAdded(deviceId); onInputDeviceAdded(deviceId);
} }
private void releaseControllerNumber(int controllerNumber) { private void releaseControllerNumber(ControllerContext context) {
LimeLog.info("Controller number "+controllerNumber+" is now available"); if (context.reservedControllerNumber) {
currentControllers &= ~(1 << controllerNumber); LimeLog.info("Controller number "+context.controllerNumber+" is now available");
currentControllers &= ~(1 << context.controllerNumber);
}
}
// Called before sending input but after we've determined that this
// is definitely a controller (not a keyboard, mouse, or something else)
private void assignControllerNumberIfNeeded(ControllerContext context) {
if (context.assignedControllerNumber) {
return;
}
LimeLog.info(context.name+" needs a controller number assigned");
if (context.name != null && context.name.contains("gpio-keys")) {
// This is the back button on Shield portable consoles
LimeLog.info("Built-in buttons hardcoded as controller 0");
context.controllerNumber = 0;
}
else if (multiControllerEnabled && context.hasJoystickAxes) {
context.controllerNumber = 0;
LimeLog.info("Reserving the next available controller number");
for (short i = 0; i < 4; i++) {
if ((currentControllers & (1 << i)) == 0) {
// Found an unused controller value
currentControllers |= (1 << i);
context.controllerNumber = i;
context.reservedControllerNumber = true;
break;
}
}
}
else {
LimeLog.info("Not reserving a controller number");
context.controllerNumber = 0;
}
LimeLog.info("Assigned as controller "+context.controllerNumber);
context.assignedControllerNumber = true;
} }
private ControllerContext createContextForDevice(InputDevice dev) { private ControllerContext createContextForDevice(InputDevice dev) {
@@ -287,18 +313,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius); LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius);
LimeLog.info("Trigger deadzone: "+context.triggerDeadzone); LimeLog.info("Trigger deadzone: "+context.triggerDeadzone);
if (devName != null && devName.equals("gpio-keys")) {
// This is the back button on Shield portable consoles
context.controllerNumber = 0;
}
else if (multiControllerEnabled && context.hasJoystickAxes) {
context.controllerNumber = assignNewControllerNumber();
}
else {
context.controllerNumber = 0;
}
LimeLog.info("Assigned as controller "+context.controllerNumber);
return context; return context;
} }
@@ -324,6 +338,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
} }
private void sendControllerInputPacket(ControllerContext context) { private void sendControllerInputPacket(ControllerContext context) {
assignControllerNumberIfNeeded(context);
conn.sendControllerInput(context.controllerNumber, context.inputMap, conn.sendControllerInput(context.controllerNumber, context.inputMap,
context.leftTrigger, context.rightTrigger, context.leftTrigger, context.rightTrigger,
context.leftStickX, context.leftStickY, context.leftStickX, context.leftStickY,
@@ -804,6 +819,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
public boolean isRemote; public boolean isRemote;
public boolean hasJoystickAxes; public boolean hasJoystickAxes;
public boolean assignedControllerNumber;
public boolean reservedControllerNumber;
public short controllerNumber; public short controllerNumber;
public short inputMap = 0x0000; public short inputMap = 0x0000;
@@ -231,7 +231,8 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
if (decodeUnit.getDataLength() <= DECODER_BUFFER_SIZE) { if (decodeUnit.getDataLength() <= DECODER_BUFFER_SIZE) {
decoderBuffer.clear(); decoderBuffer.clear();
for (ByteBufferDescriptor bbd : decodeUnit.getBufferList()) { for (ByteBufferDescriptor bbd = decodeUnit.getBufferHead();
bbd != null; bbd = bbd.nextDescriptor) {
decoderBuffer.put(bbd.data, bbd.offset, bbd.length); decoderBuffer.put(bbd.data, bbd.offset, bbd.length);
} }
@@ -241,7 +242,8 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
data = new byte[decodeUnit.getDataLength()+AvcDecoder.getInputPaddingSize()]; data = new byte[decodeUnit.getDataLength()+AvcDecoder.getInputPaddingSize()];
int offset = 0; int offset = 0;
for (ByteBufferDescriptor bbd : decodeUnit.getBufferList()) { for (ByteBufferDescriptor bbd = decodeUnit.getBufferHead();
bbd != null; bbd = bbd.nextDescriptor) {
System.arraycopy(bbd.data, bbd.offset, data, offset, bbd.length); System.arraycopy(bbd.data, bbd.offset, data, offset, bbd.length);
offset += bbd.length; offset += bbd.length;
} }
@@ -239,13 +239,13 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
inputIndex = dequeueInputBuffer(false, false); inputIndex = dequeueInputBuffer(false, false);
du = depacketizer.pollNextDecodeUnit(); du = depacketizer.pollNextDecodeUnit();
if (du != null) {
lastDuDequeueTime = System.currentTimeMillis();
notifyDuReceived(du);
}
// Stop if we can't get a DU or input buffer // Stop if we can't get a DU or input buffer
if (du == null || inputIndex == -1) { if (du == null || inputIndex == -1) {
if (du != null) {
lastDuDequeueTime = System.currentTimeMillis();
}
break; break;
} }
@@ -283,6 +283,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
du = depacketizer.pollNextDecodeUnit(); du = depacketizer.pollNextDecodeUnit();
if (du != null) { if (du != null) {
lastDuDequeueTime = System.currentTimeMillis(); lastDuDequeueTime = System.currentTimeMillis();
notifyDuReceived(du);
} }
} }
@@ -421,14 +422,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void submitDecodeUnit(DecodeUnit decodeUnit, ByteBuffer buf, int inputBufferIndex) { private void submitDecodeUnit(DecodeUnit decodeUnit, ByteBuffer buf, int inputBufferIndex) {
long currentTime = System.currentTimeMillis(); long timestampUs = System.currentTimeMillis() * 1000;
long delta = currentTime-decodeUnit.getReceiveTimestamp();
if (delta >= 0 && delta < 1000) {
totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp();
totalFrames++;
}
long timestampUs = currentTime * 1000;
if (timestampUs <= lastTimestampUs) { if (timestampUs <= lastTimestampUs) {
// We can't submit multiple buffers with the same timestamp // We can't submit multiple buffers with the same timestamp
// so bump it up by one before queuing // so bump it up by one before queuing
@@ -452,7 +446,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
boolean needsSpsReplay = false; boolean needsSpsReplay = false;
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) { if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
ByteBufferDescriptor header = decodeUnit.getBufferList().get(0); ByteBufferDescriptor header = decodeUnit.getBufferHead();
if (header.data[header.offset+4] == 0x67) { if (header.data[header.offset+4] == 0x67) {
numSpsIn++; numSpsIn++;
@@ -537,8 +531,8 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
} }
// Copy data from our buffer list into the input buffer // Copy data from our buffer list into the input buffer
for (ByteBufferDescriptor desc : decodeUnit.getBufferList()) for (ByteBufferDescriptor desc = decodeUnit.getBufferHead();
{ desc != null; desc = desc.nextDescriptor) {
buf.put(desc.data, desc.offset, desc.length); buf.put(desc.data, desc.offset, desc.length);
} }
@@ -614,10 +608,21 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
return decoderName; return decoderName;
} }
private void notifyDuReceived(DecodeUnit du) {
long currentTime = System.currentTimeMillis();
long delta = currentTime-du.getReceiveTimestamp();
if (delta >= 0 && delta < 1000) {
totalTimeMs += currentTime-du.getReceiveTimestamp();
totalFrames++;
}
}
@Override @Override
public void directSubmitDecodeUnit(DecodeUnit du) { public void directSubmitDecodeUnit(DecodeUnit du) {
int inputIndex; int inputIndex;
notifyDuReceived(du);
for (;;) { for (;;) {
try { try {
inputIndex = dequeueInputBuffer(true, true); inputIndex = dequeueInputBuffer(true, true);
@@ -39,6 +39,7 @@ public class MediaCodecHelper {
directSubmitPrefixes.add("omx.intel"); directSubmitPrefixes.add("omx.intel");
directSubmitPrefixes.add("omx.brcm"); directSubmitPrefixes.add("omx.brcm");
directSubmitPrefixes.add("omx.TI"); directSubmitPrefixes.add("omx.TI");
directSubmitPrefixes.add("omx.arc");
} }
static { static {
@@ -81,6 +81,7 @@ public class ComputerManagerService extends Service {
if (!pollComputer(details)) { if (!pollComputer(details)) {
if (!newPc && offlineCount < OFFLINE_POLL_TRIES) { if (!newPc && offlineCount < OFFLINE_POLL_TRIES) {
// Return without calling the listener // Return without calling the listener
releaseLocalDatabaseReference();
return false; return false;
} }
@@ -40,7 +40,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
dp = LARGE_WIDTH_DP; dp = LARGE_WIDTH_DP;
} }
double scalingDivisor = ART_WIDTH_PX / (dp * (dpi / 160)); double scalingDivisor = ART_WIDTH_PX / (dp * (dpi / 160.0));
if (scalingDivisor < 1.0) { if (scalingDivisor < 1.0) {
// We don't want to make them bigger before draw-time // We don't want to make them bigger before draw-time
scalingDivisor = 1.0; scalingDivisor = 1.0;
@@ -10,6 +10,7 @@ import android.widget.ImageView;
import com.limelight.nvstream.http.ComputerDetails; import com.limelight.nvstream.http.ComputerDetails;
import com.limelight.nvstream.http.NvApp; import com.limelight.nvstream.http.NvApp;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@@ -97,6 +98,11 @@ public class CachedAppAssetLoader {
// Write the stream straight to disk // Write the stream straight to disk
diskLoader.populateCacheWithStream(tuple, in); diskLoader.populateCacheWithStream(tuple, in);
// Close the network input stream
try {
in.close();
} catch (IOException ignored) {}
// If there's a task associated with this load, we should return the bitmap // If there's a task associated with this load, we should return the bitmap
if (task != null) { if (task != null) {
return diskLoader.loadBitmapFromCache(tuple, (int) scalingDivider); return diskLoader.loadBitmapFromCache(tuple, (int) scalingDivider);
@@ -61,6 +61,10 @@ public class CacheHelper {
sb.append(buf, 0, bytesRead); sb.append(buf, 0, bytesRead);
} }
try {
in.close();
} catch (IOException ignored) {}
return sb.toString(); return sb.toString();
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 65 KiB

+1 -1
View File
@@ -102,7 +102,7 @@
<string name="category_ui_settings">Impostazioni Interfaccia</string> <string name="category_ui_settings">Impostazioni Interfaccia</string>
<string name="title_language_list">Lingua</string> <string name="title_language_list">Lingua</string>
<string name="summary_language_list">Lingua da usare in Limelight</string> <string name="summary_language_list">Lingua da usare in Moonlight</string>
<string name="title_checkbox_list_mode">Usa lista invece della griglia</string> <string name="title_checkbox_list_mode">Usa lista invece della griglia</string>
<string name="summary_checkbox_list_mode">Visualizza applicazioni e computers in una lista invece di una griglia</string> <string name="summary_checkbox_list_mode">Visualizza applicazioni e computers in una lista invece di una griglia</string>
<string name="title_checkbox_small_icon_mode">Usa icone piccole</string> <string name="title_checkbox_small_icon_mode">Usa icone piccole</string>
+3 -3
View File
@@ -44,9 +44,9 @@
<string name="conn_establishing_title">Establishing Connection</string> <string name="conn_establishing_title">Establishing Connection</string>
<string name="conn_establishing_msg">Starting connection</string> <string name="conn_establishing_msg">Starting connection</string>
<string name="conn_metered">Warning: Your active network connection is metered!</string> <string name="conn_metered">Warning: Your active network connection is metered!</string>
<string name="conn_client_latency">Average client-side frame latency:</string> <string name="conn_client_latency">Average frame decoding latency:</string>
<string name="conn_client_latency_hw">hardware decoder latency:</string> <string name="conn_client_latency_hw">hardware decoder latency:</string>
<string name="conn_hardware_latency">Average hardware decoder latency:</string> <string name="conn_hardware_latency">Average hardware decoding latency:</string>
<string name="conn_starting">Starting</string> <string name="conn_starting">Starting</string>
<string name="conn_error_title">Connection Error</string> <string name="conn_error_title">Connection Error</string>
<string name="conn_error_msg">Failed to start</string> <string name="conn_error_msg">Failed to start</string>
@@ -102,7 +102,7 @@
<string name="category_ui_settings">UI Settings</string> <string name="category_ui_settings">UI Settings</string>
<string name="title_language_list">Language</string> <string name="title_language_list">Language</string>
<string name="summary_language_list">Language to use for Limelight</string> <string name="summary_language_list">Language to use for Moonlight</string>
<string name="title_checkbox_list_mode">Use lists instead of grids</string> <string name="title_checkbox_list_mode">Use lists instead of grids</string>
<string name="summary_checkbox_list_mode">Display apps and PCs in lists instead of grids</string> <string name="summary_checkbox_list_mode">Display apps and PCs in lists instead of grids</string>
<string name="title_checkbox_small_icon_mode">Use small icons</string> <string name="title_checkbox_small_icon_mode">Use small icons</string>
+1 -1
View File
@@ -2,5 +2,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Non-root application name --> <!-- Non-root application name -->
<application android:label="Limelight" /> <application android:label="Moonlight" />
</manifest> </manifest>
+1 -1
View File
@@ -5,5 +5,5 @@
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" /> <uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<!-- Root application name --> <!-- Root application name -->
<application android:label="Limelight (Root)" /> <application android:label="Moonlight (Root)" />
</manifest> </manifest>
File renamed without changes.