mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-25 22:13:04 +00:00
292 lines
6.9 KiB
Java
292 lines
6.9 KiB
Java
package com.limelight.nvstream;
|
|
|
|
import java.io.IOException;
|
|
import java.net.InetAddress;
|
|
import java.net.NetworkInterface;
|
|
import java.net.SocketException;
|
|
import java.net.UnknownHostException;
|
|
import java.util.Enumeration;
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
|
|
import android.content.Context;
|
|
import android.net.ConnectivityManager;
|
|
import android.view.Surface;
|
|
import android.widget.Toast;
|
|
|
|
import com.limelight.Game;
|
|
import com.limelight.nvstream.input.NvController;
|
|
|
|
public class NvConnection {
|
|
private String host;
|
|
private Game activity;
|
|
|
|
private NvControl controlStream;
|
|
private NvController inputStream;
|
|
private Surface video;
|
|
private NvVideoStream videoStream = new NvVideoStream();
|
|
private NvAudioStream audioStream = new NvAudioStream();
|
|
|
|
private ThreadPoolExecutor threadPool;
|
|
|
|
public NvConnection(String host, Game activity, Surface video)
|
|
{
|
|
this.host = host;
|
|
this.activity = activity;
|
|
this.video = video;
|
|
this.threadPool = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());
|
|
}
|
|
|
|
public static String getMacAddressString() throws SocketException {
|
|
Enumeration<NetworkInterface> ifaceList;
|
|
NetworkInterface selectedIface = null;
|
|
|
|
// First look for a WLAN interface (since those generally aren't removable)
|
|
ifaceList = NetworkInterface.getNetworkInterfaces();
|
|
while (selectedIface == null && ifaceList.hasMoreElements()) {
|
|
NetworkInterface iface = ifaceList.nextElement();
|
|
|
|
if (iface.getName().startsWith("wlan") &&
|
|
iface.getHardwareAddress() != null) {
|
|
selectedIface = iface;
|
|
}
|
|
}
|
|
|
|
// If we didn't find that, look for an Ethernet interface
|
|
ifaceList = NetworkInterface.getNetworkInterfaces();
|
|
while (selectedIface == null && ifaceList.hasMoreElements()) {
|
|
NetworkInterface iface = ifaceList.nextElement();
|
|
|
|
if (iface.getName().startsWith("eth") &&
|
|
iface.getHardwareAddress() != null) {
|
|
selectedIface = iface;
|
|
}
|
|
}
|
|
|
|
// Now just find something with a MAC address
|
|
ifaceList = NetworkInterface.getNetworkInterfaces();
|
|
while (selectedIface == null && ifaceList.hasMoreElements()) {
|
|
NetworkInterface iface = ifaceList.nextElement();
|
|
|
|
if (iface.getHardwareAddress() != null) {
|
|
selectedIface = ifaceList.nextElement();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selectedIface == null) {
|
|
return null;
|
|
}
|
|
|
|
byte[] macAddress = selectedIface.getHardwareAddress();
|
|
if (macAddress != null) {
|
|
StringBuilder addrStr = new StringBuilder();
|
|
for (int i = 0; i < macAddress.length; i++) {
|
|
addrStr.append(String.format("%02x", macAddress[i]));
|
|
if (i != macAddress.length - 1) {
|
|
addrStr.append(':');
|
|
}
|
|
}
|
|
return addrStr.toString();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public void stop()
|
|
{
|
|
threadPool.shutdownNow();
|
|
|
|
videoStream.abort();
|
|
audioStream.abort();
|
|
|
|
if (controlStream != null) {
|
|
controlStream.abort();
|
|
}
|
|
|
|
if (inputStream != null) {
|
|
inputStream.close();
|
|
inputStream = null;
|
|
}
|
|
}
|
|
|
|
public void trim()
|
|
{
|
|
videoStream.trim();
|
|
audioStream.trim();
|
|
}
|
|
|
|
public void start()
|
|
{
|
|
new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
checkDataConnection();
|
|
|
|
try {
|
|
host = InetAddress.getByName(host).getHostAddress();
|
|
} catch (UnknownHostException e) {
|
|
e.printStackTrace();
|
|
displayToast(e.getMessage());
|
|
return;
|
|
}
|
|
|
|
try {
|
|
startSteamBigPicture();
|
|
performHandshake();
|
|
videoStream.startVideoStream(host, video);
|
|
audioStream.startAudioStream(host);
|
|
beginControlStream();
|
|
controlStream.startJitterPackets();
|
|
startController();
|
|
activity.hideSystemUi();
|
|
} catch (XmlPullParserException e) {
|
|
e.printStackTrace();
|
|
displayToast(e.getMessage());
|
|
stop();
|
|
} catch (IOException e) {
|
|
displayToast(e.getMessage());
|
|
stop();
|
|
}
|
|
}
|
|
}).start();
|
|
}
|
|
|
|
private void checkDataConnection()
|
|
{
|
|
ConnectivityManager mgr = (ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
if (mgr.isActiveNetworkMetered()) {
|
|
displayToast("Warning: Your active network connection is metered!");
|
|
}
|
|
}
|
|
|
|
public void sendMouseMove(final short deltaX, final short deltaY)
|
|
{
|
|
if (inputStream == null)
|
|
return;
|
|
|
|
threadPool.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
inputStream.sendMouseMove(deltaX, deltaY);
|
|
} catch (IOException e) {
|
|
displayToast(e.getMessage());
|
|
NvConnection.this.stop();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void sendMouseButtonDown()
|
|
{
|
|
if (inputStream == null)
|
|
return;
|
|
|
|
threadPool.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
inputStream.sendMouseButtonDown();
|
|
} catch (IOException e) {
|
|
displayToast(e.getMessage());
|
|
NvConnection.this.stop();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void sendMouseButtonUp()
|
|
{
|
|
if (inputStream == null)
|
|
return;
|
|
|
|
threadPool.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
inputStream.sendMouseButtonUp();
|
|
} catch (IOException e) {
|
|
displayToast(e.getMessage());
|
|
NvConnection.this.stop();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void sendControllerInput(final short buttonFlags,
|
|
final byte leftTrigger, final byte rightTrigger,
|
|
final short leftStickX, final short leftStickY,
|
|
final short rightStickX, final short rightStickY)
|
|
{
|
|
if (inputStream == null)
|
|
return;
|
|
|
|
threadPool.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
inputStream.sendControllerInput(buttonFlags, leftTrigger,
|
|
rightTrigger, leftStickX, leftStickY,
|
|
rightStickX, rightStickY);
|
|
} catch (IOException e) {
|
|
displayToast(e.getMessage());
|
|
NvConnection.this.stop();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void displayToast(final String message)
|
|
{
|
|
activity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
|
|
}
|
|
});
|
|
}
|
|
|
|
private void startSteamBigPicture() throws XmlPullParserException, IOException
|
|
{
|
|
NvHTTP h = new NvHTTP(host, getMacAddressString());
|
|
|
|
if (!h.getPairState())
|
|
{
|
|
displayToast("Device not paired with computer");
|
|
return;
|
|
}
|
|
|
|
int sessionId = h.getSessionId();
|
|
int appId = h.getSteamAppId(sessionId);
|
|
|
|
System.out.println("Starting game session");
|
|
int gameSession = h.launchApp(sessionId, appId);
|
|
System.out.println("Started game session: "+gameSession);
|
|
}
|
|
|
|
private void performHandshake() throws UnknownHostException, IOException
|
|
{
|
|
System.out.println("Starting handshake");
|
|
NvHandshake.performHandshake(host);
|
|
System.out.println("Handshake complete");
|
|
}
|
|
|
|
private void beginControlStream() throws UnknownHostException, IOException
|
|
{
|
|
controlStream = new NvControl(host);
|
|
|
|
System.out.println("Starting control");
|
|
controlStream.start();
|
|
}
|
|
|
|
private void startController() throws UnknownHostException, IOException
|
|
{
|
|
System.out.println("Starting input");
|
|
inputStream = new NvController(host);
|
|
}
|
|
}
|