Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d9cf71517 | |||
| 2160e87fef | |||
| 88249ba8aa | |||
| 2856617fb3 | |||
| d822980d5a | |||
| b5ba59b413 | |||
| 1148e0163c | |||
| cf36c7adb1 | |||
| eac6998e17 | |||
| 17afbffdb5 | |||
| 072a439c2d |
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
[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
|
||||
|
||||
@@ -18,7 +18,7 @@ Check our [wiki](https://github.com/limelight-stream/limelight-android/wiki) for
|
||||
|
||||
##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)
|
||||
* 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
|
||||
* If you are connecting from outside the same network, turn on internet
|
||||
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
|
||||
* Tap your PC again to view the list of apps to stream
|
||||
* Play games!
|
||||
@@ -46,8 +46,6 @@ This project is being actively developed at [XDA Developers](http://forum.xda-de
|
||||
2. Write code
|
||||
3. Send Pull Requests
|
||||
|
||||
Check out our [website](http://limelight-stream.com) for project links and information.
|
||||
|
||||
##Authors
|
||||
|
||||
* [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)
|
||||
* [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).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
|
||||
versionName "3.1.3"
|
||||
versionCode = 58
|
||||
versionName "3.1.5"
|
||||
versionCode = 60
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
|
||||
@@ -97,18 +97,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
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
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
// Nothing happening here yet
|
||||
@@ -119,7 +107,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
for (Map.Entry<String, ControllerContext> device : contexts.entrySet()) {
|
||||
if (device.getValue().id == deviceId) {
|
||||
LimeLog.info("Removed controller: "+device.getValue().name);
|
||||
releaseControllerNumber(device.getValue().controllerNumber);
|
||||
releaseControllerNumber(device.getValue());
|
||||
contexts.remove(device.getKey());
|
||||
return;
|
||||
}
|
||||
@@ -133,9 +121,47 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
onInputDeviceAdded(deviceId);
|
||||
}
|
||||
|
||||
private void releaseControllerNumber(int controllerNumber) {
|
||||
LimeLog.info("Controller number "+controllerNumber+" is now available");
|
||||
currentControllers &= ~(1 << controllerNumber);
|
||||
private void releaseControllerNumber(ControllerContext context) {
|
||||
if (context.reservedControllerNumber) {
|
||||
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) {
|
||||
@@ -287,18 +313,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -324,6 +338,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
}
|
||||
|
||||
private void sendControllerInputPacket(ControllerContext context) {
|
||||
assignControllerNumberIfNeeded(context);
|
||||
conn.sendControllerInput(context.controllerNumber, context.inputMap,
|
||||
context.leftTrigger, context.rightTrigger,
|
||||
context.leftStickX, context.leftStickY,
|
||||
@@ -804,6 +819,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
public boolean isRemote;
|
||||
public boolean hasJoystickAxes;
|
||||
|
||||
public boolean assignedControllerNumber;
|
||||
public boolean reservedControllerNumber;
|
||||
public short controllerNumber;
|
||||
|
||||
public short inputMap = 0x0000;
|
||||
|
||||
@@ -231,7 +231,8 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
if (decodeUnit.getDataLength() <= DECODER_BUFFER_SIZE) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -241,7 +242,8 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
data = new byte[decodeUnit.getDataLength()+AvcDecoder.getInputPaddingSize()];
|
||||
|
||||
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);
|
||||
offset += bbd.length;
|
||||
}
|
||||
|
||||
@@ -239,13 +239,13 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
inputIndex = dequeueInputBuffer(false, false);
|
||||
du = depacketizer.pollNextDecodeUnit();
|
||||
if (du != null) {
|
||||
lastDuDequeueTime = System.currentTimeMillis();
|
||||
notifyDuReceived(du);
|
||||
}
|
||||
|
||||
// Stop if we can't get a DU or input buffer
|
||||
if (du == null || inputIndex == -1) {
|
||||
if (du != null) {
|
||||
lastDuDequeueTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -283,6 +283,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
du = depacketizer.pollNextDecodeUnit();
|
||||
if (du != null) {
|
||||
lastDuDequeueTime = System.currentTimeMillis();
|
||||
notifyDuReceived(du);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,14 +422,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void submitDecodeUnit(DecodeUnit decodeUnit, ByteBuffer buf, int inputBufferIndex) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long delta = currentTime-decodeUnit.getReceiveTimestamp();
|
||||
if (delta >= 0 && delta < 1000) {
|
||||
totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp();
|
||||
totalFrames++;
|
||||
}
|
||||
|
||||
long timestampUs = currentTime * 1000;
|
||||
long timestampUs = System.currentTimeMillis() * 1000;
|
||||
if (timestampUs <= lastTimestampUs) {
|
||||
// We can't submit multiple buffers with the same timestamp
|
||||
// so bump it up by one before queuing
|
||||
@@ -452,7 +446,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
boolean needsSpsReplay = false;
|
||||
|
||||
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) {
|
||||
numSpsIn++;
|
||||
|
||||
@@ -537,8 +531,8 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -614,10 +608,21 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
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
|
||||
public void directSubmitDecodeUnit(DecodeUnit du) {
|
||||
int inputIndex;
|
||||
|
||||
notifyDuReceived(du);
|
||||
|
||||
for (;;) {
|
||||
try {
|
||||
inputIndex = dequeueInputBuffer(true, true);
|
||||
|
||||
@@ -39,6 +39,7 @@ public class MediaCodecHelper {
|
||||
directSubmitPrefixes.add("omx.intel");
|
||||
directSubmitPrefixes.add("omx.brcm");
|
||||
directSubmitPrefixes.add("omx.TI");
|
||||
directSubmitPrefixes.add("omx.arc");
|
||||
}
|
||||
|
||||
static {
|
||||
|
||||
@@ -81,6 +81,7 @@ public class ComputerManagerService extends Service {
|
||||
if (!pollComputer(details)) {
|
||||
if (!newPc && offlineCount < OFFLINE_POLL_TRIES) {
|
||||
// Return without calling the listener
|
||||
releaseLocalDatabaseReference();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
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) {
|
||||
// We don't want to make them bigger before draw-time
|
||||
scalingDivisor = 1.0;
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.widget.ImageView;
|
||||
import com.limelight.nvstream.http.ComputerDetails;
|
||||
import com.limelight.nvstream.http.NvApp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@@ -97,6 +98,11 @@ public class CachedAppAssetLoader {
|
||||
// Write the stream straight to disk
|
||||
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 (task != null) {
|
||||
return diskLoader.loadBitmapFromCache(tuple, (int) scalingDivider);
|
||||
|
||||
@@ -61,6 +61,10 @@ public class CacheHelper {
|
||||
sb.append(buf, 0, bytesRead);
|
||||
}
|
||||
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 65 KiB |
@@ -102,7 +102,7 @@
|
||||
|
||||
<string name="category_ui_settings">Impostazioni Interfaccia</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="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>
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
<string name="conn_establishing_title">Establishing 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_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_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_error_title">Connection Error</string>
|
||||
<string name="conn_error_msg">Failed to start</string>
|
||||
@@ -102,7 +102,7 @@
|
||||
|
||||
<string name="category_ui_settings">UI Settings</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="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>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Non-root application name -->
|
||||
<application android:label="Limelight" />
|
||||
<application android:label="Moonlight" />
|
||||
</manifest>
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
||||
|
||||
<!-- Root application name -->
|
||||
<application android:label="Limelight (Root)" />
|
||||
<application android:label="Moonlight (Root)" />
|
||||
</manifest>
|
||||
|
||||