mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 03:23:07 +00:00
Add UI indications of the connection status and failure
This commit is contained in:
parent
c88d23001e
commit
4d5849f448
@ -2,7 +2,9 @@ package com.limelight;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
@ -88,26 +90,30 @@ public class Connection extends Activity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvHTTP httpConn = new NvHTTP(hostText.getText().toString(), macAddress);
|
NvHTTP httpConn;
|
||||||
|
|
||||||
String message;
|
String message;
|
||||||
try {
|
try {
|
||||||
if (httpConn.getPairState()) {
|
httpConn = new NvHTTP(InetAddress.getByName(hostText.getText().toString()), macAddress);
|
||||||
message = "Already paired";
|
try {
|
||||||
}
|
if (httpConn.getPairState()) {
|
||||||
else {
|
message = "Already paired";
|
||||||
int session = httpConn.getSessionId();
|
|
||||||
if (session == 0) {
|
|
||||||
message = "Pairing was declined by the target";
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
message = "Pairing was successful";
|
int session = httpConn.getSessionId();
|
||||||
|
if (session == 0) {
|
||||||
|
message = "Pairing was declined by the target";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message = "Pairing was successful";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
message = e.getMessage();
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
message = e.getMessage();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (UnknownHostException e1) {
|
||||||
message = e.getMessage();
|
message = "Failed to resolve host";
|
||||||
} catch (XmlPullParserException e) {
|
|
||||||
message = e.getMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String toastMessage = message;
|
final String toastMessage = message;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package com.limelight;
|
package com.limelight;
|
||||||
|
|
||||||
import com.limelight.nvstream.NvConnection;
|
import com.limelight.nvstream.NvConnection;
|
||||||
|
import com.limelight.nvstream.NvConnectionListener;
|
||||||
import com.limelight.nvstream.input.NvControllerPacket;
|
import com.limelight.nvstream.input.NvControllerPacket;
|
||||||
|
import com.limelight.utils.Dialog;
|
||||||
|
import com.limelight.utils.SpinnerDialog;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
@ -18,7 +21,7 @@ import android.view.Window;
|
|||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
|
||||||
public class Game extends Activity implements OnGenericMotionListener, OnTouchListener {
|
public class Game extends Activity implements OnGenericMotionListener, OnTouchListener, NvConnectionListener {
|
||||||
private short inputMap = 0x0000;
|
private short inputMap = 0x0000;
|
||||||
private byte leftTrigger = 0x00;
|
private byte leftTrigger = 0x00;
|
||||||
private byte rightTrigger = 0x00;
|
private byte rightTrigger = 0x00;
|
||||||
@ -33,6 +36,8 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
|
|||||||
private boolean hasMoved = false;
|
private boolean hasMoved = false;
|
||||||
|
|
||||||
private NvConnection conn;
|
private NvConnection conn;
|
||||||
|
private SpinnerDialog spinner;
|
||||||
|
private boolean displayedFailureDialog = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -59,6 +64,9 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
|
|||||||
sh.setFixedSize(1280, 720);
|
sh.setFixedSize(1280, 720);
|
||||||
sh.setFormat(PixelFormat.RGBX_8888);
|
sh.setFormat(PixelFormat.RGBX_8888);
|
||||||
|
|
||||||
|
// Start the spinner
|
||||||
|
spinner = SpinnerDialog.displayDialog(this, "Establishing Connection", "Starting connection", true);
|
||||||
|
|
||||||
// Start the connection
|
// Start the connection
|
||||||
conn = new NvConnection(Game.this.getIntent().getStringExtra("host"), Game.this, sv.getHolder().getSurface());
|
conn = new NvConnection(Game.this.getIntent().getStringExtra("host"), Game.this, sv.getHolder().getSurface());
|
||||||
conn.start();
|
conn.start();
|
||||||
@ -94,6 +102,13 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
|
|||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
SpinnerDialog.closeDialogs();
|
||||||
|
Dialog.closeDialogs();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
@ -439,4 +454,42 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
|
|||||||
// Send it to the activity's touch event handler
|
// Send it to the activity's touch event handler
|
||||||
return onTouchEvent(event);
|
return onTouchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stageStarting(Stage stage) {
|
||||||
|
if (spinner != null) {
|
||||||
|
spinner.setMessage("Starting "+stage.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stageComplete(Stage stage) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stageFailed(Stage stage) {
|
||||||
|
spinner.dismiss();
|
||||||
|
spinner = null;
|
||||||
|
|
||||||
|
if (!displayedFailureDialog) {
|
||||||
|
displayedFailureDialog = true;
|
||||||
|
Dialog.displayDialog(this, "Connection Error", "Starting "+stage.getName()+" failed", true);
|
||||||
|
conn.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectionTerminated() {
|
||||||
|
if (!displayedFailureDialog) {
|
||||||
|
displayedFailureDialog = true;
|
||||||
|
Dialog.displayDialog(this, "Connection Terminated", "The connection failed unexpectedly", true);
|
||||||
|
conn.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectionStarted() {
|
||||||
|
spinner.dismiss();
|
||||||
|
spinner = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import java.net.DatagramPacket;
|
|||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
@ -35,6 +34,15 @@ public class NvAudioStream {
|
|||||||
|
|
||||||
private boolean aborting = false;
|
private boolean aborting = false;
|
||||||
|
|
||||||
|
private InetAddress host;
|
||||||
|
private NvConnectionListener listener;
|
||||||
|
|
||||||
|
public NvAudioStream(InetAddress host, NvConnectionListener listener)
|
||||||
|
{
|
||||||
|
this.host = host;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
public void abort()
|
public void abort()
|
||||||
{
|
{
|
||||||
if (aborting) {
|
if (aborting) {
|
||||||
@ -66,40 +74,25 @@ public class NvAudioStream {
|
|||||||
threads.clear();
|
threads.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startAudioStream(final String host)
|
public void startAudioStream() throws SocketException
|
||||||
{
|
{
|
||||||
new Thread(new Runnable() {
|
setupRtpSession();
|
||||||
|
|
||||||
@Override
|
setupAudio();
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
setupRtpSession(host);
|
|
||||||
} catch (SocketException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupAudio();
|
startReceiveThread();
|
||||||
|
|
||||||
startReceiveThread();
|
startDepacketizerThread();
|
||||||
|
|
||||||
startDepacketizerThread();
|
startDecoderThread();
|
||||||
|
|
||||||
startDecoderThread();
|
startUdpPingThread();
|
||||||
|
|
||||||
startUdpPingThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupRtpSession(String host) throws SocketException, UnknownHostException
|
private void setupRtpSession() throws SocketException
|
||||||
{
|
{
|
||||||
rtp = new DatagramSocket(RTP_PORT);
|
rtp = new DatagramSocket(RTP_PORT);
|
||||||
rtp.connect(InetAddress.getByName(host), RTP_PORT);
|
rtp.connect(host, RTP_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupAudio()
|
private void setupAudio()
|
||||||
@ -108,12 +101,8 @@ public class NvAudioStream {
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = OpusDecoder.init();
|
err = OpusDecoder.init();
|
||||||
if (err == 0) {
|
if (err != 0) {
|
||||||
System.out.println("Opus decoder initialized");
|
throw new IllegalStateException("Opus decoder failed to initialize");
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.err.println("Opus decoder init failed: "+err);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (OpusDecoder.getChannelCount())
|
switch (OpusDecoder.getChannelCount())
|
||||||
@ -125,8 +114,7 @@ public class NvAudioStream {
|
|||||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.err.println("Unsupported channel count");
|
throw new IllegalStateException("Opus decoder returned unhandled channel count");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||||
@ -153,7 +141,7 @@ public class NvAudioStream {
|
|||||||
try {
|
try {
|
||||||
packet = packets.take();
|
packet = packets.take();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +166,7 @@ public class NvAudioStream {
|
|||||||
try {
|
try {
|
||||||
samples = depacketizer.getNextDecodedData();
|
samples = depacketizer.getNextDecodedData();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +192,7 @@ public class NvAudioStream {
|
|||||||
try {
|
try {
|
||||||
rtp.receive(packet);
|
rtp.receive(packet);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,14 +228,14 @@ public class NvAudioStream {
|
|||||||
try {
|
try {
|
||||||
rtp.send(pingPacket);
|
rtp.send(pingPacket);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,10 @@ public class NvComputer {
|
|||||||
this.uniqueID = uniqueID;
|
this.uniqueID = uniqueID;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.nvHTTP = new NvHTTP(this.ipAddressString, NvConnection.getMacAddressString());
|
this.nvHTTP = new NvHTTP(this.ipAddress, NvConnection.getMacAddressString());
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
Log.e("NvComputer Constructor", "Unable to get MAC Address " + e.getMessage());
|
Log.e("NvComputer Constructor", "Unable to get MAC Address " + e.getMessage());
|
||||||
this.nvHTTP = new NvHTTP(this.ipAddressString, "00:00:00:00:00:00");
|
this.nvHTTP = new NvHTTP(this.ipAddress, "00:00:00:00:00:00");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updatePairState();
|
this.updatePairState();
|
||||||
|
@ -23,18 +23,21 @@ import com.limelight.nvstream.input.NvController;
|
|||||||
public class NvConnection {
|
public class NvConnection {
|
||||||
private String host;
|
private String host;
|
||||||
private Game activity;
|
private Game activity;
|
||||||
|
private NvConnectionListener listener;
|
||||||
|
|
||||||
|
private InetAddress hostAddr;
|
||||||
private NvControl controlStream;
|
private NvControl controlStream;
|
||||||
private NvController inputStream;
|
private NvController inputStream;
|
||||||
private Surface video;
|
private Surface video;
|
||||||
private NvVideoStream videoStream;
|
private NvVideoStream videoStream;
|
||||||
private NvAudioStream audioStream = new NvAudioStream();
|
private NvAudioStream audioStream;
|
||||||
|
|
||||||
private ThreadPoolExecutor threadPool;
|
private ThreadPoolExecutor threadPool;
|
||||||
|
|
||||||
public NvConnection(String host, Game activity, Surface video)
|
public NvConnection(String host, Game activity, Surface video)
|
||||||
{
|
{
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
this.listener = activity;
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.video = video;
|
this.video = video;
|
||||||
this.threadPool = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());
|
this.threadPool = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());
|
||||||
@ -117,6 +120,106 @@ public class NvConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean startSteamBigPicture() throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
NvHTTP h = new NvHTTP(hostAddr, getMacAddressString());
|
||||||
|
|
||||||
|
if (!h.getPairState()) {
|
||||||
|
displayToast("Device not paired with computer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sessionId = h.getSessionId();
|
||||||
|
int appId = h.getSteamAppId(sessionId);
|
||||||
|
|
||||||
|
h.launchApp(sessionId, appId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startControlStream() throws IOException
|
||||||
|
{
|
||||||
|
controlStream = new NvControl(hostAddr, listener);
|
||||||
|
controlStream.initialize();
|
||||||
|
controlStream.start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startVideoStream() throws IOException
|
||||||
|
{
|
||||||
|
videoStream = new NvVideoStream(hostAddr, listener, controlStream);
|
||||||
|
videoStream.startVideoStream(video);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startAudioStream() throws IOException
|
||||||
|
{
|
||||||
|
audioStream = new NvAudioStream(hostAddr, listener);
|
||||||
|
audioStream.startAudioStream();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startInputConnection() throws IOException
|
||||||
|
{
|
||||||
|
inputStream = new NvController(hostAddr);
|
||||||
|
inputStream.initialize();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void establishConnection() {
|
||||||
|
for (NvConnectionListener.Stage currentStage : NvConnectionListener.Stage.values())
|
||||||
|
{
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
|
listener.stageStarting(currentStage);
|
||||||
|
try {
|
||||||
|
switch (currentStage)
|
||||||
|
{
|
||||||
|
case LAUNCH_APP:
|
||||||
|
success = startSteamBigPicture();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HANDSHAKE:
|
||||||
|
success = NvHandshake.performHandshake(hostAddr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONTROL_START:
|
||||||
|
success = startControlStream();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIDEO_START:
|
||||||
|
success = startVideoStream();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIO_START:
|
||||||
|
success = startAudioStream();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONTROL_START2:
|
||||||
|
controlStream.startJitterPackets();
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INPUT_START:
|
||||||
|
success = startInputConnection();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
listener.stageComplete(currentStage);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listener.stageFailed(currentStage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.connectionStarted();
|
||||||
|
}
|
||||||
|
|
||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@ -125,31 +228,16 @@ public class NvConnection {
|
|||||||
checkDataConnection();
|
checkDataConnection();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
host = InetAddress.getByName(host).getHostAddress();
|
hostAddr = InetAddress.getByName(host);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
e.printStackTrace();
|
|
||||||
displayToast(e.getMessage());
|
displayToast(e.getMessage());
|
||||||
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
establishConnection();
|
||||||
startSteamBigPicture();
|
|
||||||
performHandshake();
|
activity.hideSystemUi();
|
||||||
beginControlStream();
|
|
||||||
videoStream = new NvVideoStream(controlStream);
|
|
||||||
videoStream.startVideoStream(host, video);
|
|
||||||
audioStream.startAudioStream(host);
|
|
||||||
controlStream.startJitterPackets();
|
|
||||||
startController();
|
|
||||||
activity.hideSystemUi();
|
|
||||||
} catch (XmlPullParserException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
displayToast(e.getMessage());
|
|
||||||
stop();
|
|
||||||
} catch (IOException e) {
|
|
||||||
displayToast(e.getMessage());
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
@ -173,8 +261,7 @@ public class NvConnection {
|
|||||||
try {
|
try {
|
||||||
inputStream.sendMouseMove(deltaX, deltaY);
|
inputStream.sendMouseMove(deltaX, deltaY);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
displayToast(e.getMessage());
|
listener.connectionTerminated();
|
||||||
NvConnection.this.stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -191,8 +278,7 @@ public class NvConnection {
|
|||||||
try {
|
try {
|
||||||
inputStream.sendMouseButtonDown();
|
inputStream.sendMouseButtonDown();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
displayToast(e.getMessage());
|
listener.connectionTerminated();
|
||||||
NvConnection.this.stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -209,8 +295,7 @@ public class NvConnection {
|
|||||||
try {
|
try {
|
||||||
inputStream.sendMouseButtonUp();
|
inputStream.sendMouseButtonUp();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
displayToast(e.getMessage());
|
listener.connectionTerminated();
|
||||||
NvConnection.this.stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -232,8 +317,7 @@ public class NvConnection {
|
|||||||
rightTrigger, leftStickX, leftStickY,
|
rightTrigger, leftStickX, leftStickY,
|
||||||
rightStickX, rightStickY);
|
rightStickX, rightStickY);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
displayToast(e.getMessage());
|
listener.connectionTerminated();
|
||||||
NvConnection.this.stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -248,43 +332,4 @@ public class NvConnection {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
30
src/com/limelight/nvstream/NvConnectionListener.java
Normal file
30
src/com/limelight/nvstream/NvConnectionListener.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package com.limelight.nvstream;
|
||||||
|
|
||||||
|
public interface NvConnectionListener {
|
||||||
|
|
||||||
|
public enum Stage {
|
||||||
|
LAUNCH_APP("app"),
|
||||||
|
HANDSHAKE("handshake"),
|
||||||
|
CONTROL_START("control connection"),
|
||||||
|
VIDEO_START("video stream"),
|
||||||
|
AUDIO_START("audio stream"),
|
||||||
|
CONTROL_START2("control connection"),
|
||||||
|
INPUT_START("input connection");
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Stage(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void stageStarting(Stage stage);
|
||||||
|
public void stageComplete(Stage stage);
|
||||||
|
public void stageFailed(Stage stage);
|
||||||
|
|
||||||
|
public void connectionStarted();
|
||||||
|
public void connectionTerminated();
|
||||||
|
}
|
@ -3,8 +3,9 @@ package com.limelight.nvstream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
@ -14,6 +15,8 @@ public class NvControl implements ConnectionStatusListener {
|
|||||||
|
|
||||||
public static final int PORT = 47995;
|
public static final int PORT = 47995;
|
||||||
|
|
||||||
|
public static final int CONTROL_TIMEOUT = 3000;
|
||||||
|
|
||||||
public static final short PTYPE_HELLO = 0x1204;
|
public static final short PTYPE_HELLO = 0x1204;
|
||||||
public static final short PPAYLEN_HELLO = 0x0004;
|
public static final short PPAYLEN_HELLO = 0x0004;
|
||||||
public static final byte[] PPAYLOAD_HELLO =
|
public static final byte[] PPAYLOAD_HELLO =
|
||||||
@ -140,6 +143,9 @@ public class NvControl implements ConnectionStatusListener {
|
|||||||
|
|
||||||
private int seqNum;
|
private int seqNum;
|
||||||
|
|
||||||
|
private NvConnectionListener listener;
|
||||||
|
private InetAddress host;
|
||||||
|
|
||||||
private Socket s;
|
private Socket s;
|
||||||
private InputStream in;
|
private InputStream in;
|
||||||
private OutputStream out;
|
private OutputStream out;
|
||||||
@ -148,20 +154,28 @@ public class NvControl implements ConnectionStatusListener {
|
|||||||
private Thread jitterThread;
|
private Thread jitterThread;
|
||||||
private boolean aborting = false;
|
private boolean aborting = false;
|
||||||
|
|
||||||
public NvControl(String host) throws UnknownHostException, IOException
|
public NvControl(InetAddress host, NvConnectionListener listener)
|
||||||
{
|
{
|
||||||
s = new Socket(host, PORT);
|
this.listener = listener;
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize() throws IOException
|
||||||
|
{
|
||||||
|
s = new Socket();
|
||||||
|
s.setSoTimeout(CONTROL_TIMEOUT);
|
||||||
|
s.connect(new InetSocketAddress(host, PORT), CONTROL_TIMEOUT);
|
||||||
in = s.getInputStream();
|
in = s.getInputStream();
|
||||||
out = s.getOutputStream();
|
out = s.getOutputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(NvCtlPacket packet) throws IOException
|
private void sendPacket(NvCtlPacket packet) throws IOException
|
||||||
{
|
{
|
||||||
out.write(packet.toWire());
|
out.write(packet.toWire());
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NvControl.NvCtlResponse sendAndGetReply(NvCtlPacket packet) throws IOException
|
private NvControl.NvCtlResponse sendAndGetReply(NvCtlPacket packet) throws IOException
|
||||||
{
|
{
|
||||||
sendPacket(packet);
|
sendPacket(packet);
|
||||||
return new NvCtlResponse(in);
|
return new NvCtlResponse(in);
|
||||||
@ -208,15 +222,10 @@ public class NvControl implements ConnectionStatusListener {
|
|||||||
|
|
||||||
public void start() throws IOException
|
public void start() throws IOException
|
||||||
{
|
{
|
||||||
System.out.println("CTL: Sending hello");
|
|
||||||
sendHello();
|
sendHello();
|
||||||
System.out.println("CTL: Sending config");
|
|
||||||
sendConfig();
|
sendConfig();
|
||||||
System.out.println("CTL: Initial ping/pong");
|
|
||||||
pingPong();
|
pingPong();
|
||||||
System.out.println("CTL: Sending and waiting for 1405");
|
|
||||||
send1405AndGetResponse();
|
send1405AndGetResponse();
|
||||||
System.out.println("CTL: Launching heartbeat thread");
|
|
||||||
|
|
||||||
heartbeatThread = new Thread() {
|
heartbeatThread = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
@ -226,7 +235,7 @@ public class NvControl implements ConnectionStatusListener {
|
|||||||
try {
|
try {
|
||||||
sendHeartbeat();
|
sendHeartbeat();
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +243,7 @@ public class NvControl implements ConnectionStatusListener {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,14 +262,14 @@ public class NvControl implements ConnectionStatusListener {
|
|||||||
try {
|
try {
|
||||||
sendJitter();
|
sendJitter();
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ package com.limelight.nvstream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
@ -15,11 +17,16 @@ public class NvHTTP {
|
|||||||
private String macAddress;
|
private String macAddress;
|
||||||
|
|
||||||
public static final int PORT = 47989;
|
public static final int PORT = 47989;
|
||||||
|
|
||||||
|
public static final int CONNECTION_TIMEOUT = 3000;
|
||||||
|
public static final int REQUEST_TIMEOUT = 15000;
|
||||||
|
|
||||||
|
|
||||||
public String baseUrl;
|
public String baseUrl;
|
||||||
|
|
||||||
public NvHTTP(String host, String macAddress) {
|
public NvHTTP(InetAddress host, String macAddress) {
|
||||||
this.macAddress = macAddress;
|
this.macAddress = macAddress;
|
||||||
this.baseUrl = "http://" + host + ":" + PORT;
|
this.baseUrl = "http://" + host.getHostAddress() + ":" + PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getXmlString(InputStream in, String tagname)
|
private String getXmlString(InputStream in, String tagname)
|
||||||
@ -53,7 +60,12 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputStream openHttpConnection(String url) throws IOException {
|
private InputStream openHttpConnection(String url) throws IOException {
|
||||||
return new URL(url).openConnection().getInputStream();
|
URLConnection conn = new URL(url).openConnection();
|
||||||
|
conn.setConnectTimeout(CONNECTION_TIMEOUT);
|
||||||
|
conn.setDefaultUseCaches(false);
|
||||||
|
conn.setReadTimeout(REQUEST_TIMEOUT);
|
||||||
|
conn.connect();
|
||||||
|
return conn.getInputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAppVersion() throws XmlPullParserException, IOException {
|
public String getAppVersion() throws XmlPullParserException, IOException {
|
||||||
|
@ -3,15 +3,23 @@ package com.limelight.nvstream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;;
|
|
||||||
|
|
||||||
public class NvHandshake {
|
public class NvHandshake {
|
||||||
public static final int PORT = 47991;
|
public static final int PORT = 47991;
|
||||||
|
|
||||||
// android
|
public static final int HANDSHAKE_TIMEOUT = 3000;
|
||||||
|
|
||||||
public static final byte[] PLATFORM_HELLO =
|
public static final byte[] PLATFORM_HELLO =
|
||||||
{
|
{
|
||||||
|
(byte)0x07,
|
||||||
|
(byte)0x00,
|
||||||
|
(byte)0x00,
|
||||||
|
(byte)0x00,
|
||||||
|
|
||||||
|
// android in ASCII
|
||||||
(byte)0x61,
|
(byte)0x61,
|
||||||
(byte)0x6e,
|
(byte)0x6e,
|
||||||
(byte)0x64,
|
(byte)0x64,
|
||||||
@ -19,6 +27,7 @@ public class NvHandshake {
|
|||||||
(byte)0x6f,
|
(byte)0x6f,
|
||||||
(byte)0x69,
|
(byte)0x69,
|
||||||
(byte)0x64,
|
(byte)0x64,
|
||||||
|
|
||||||
(byte)0x03,
|
(byte)0x03,
|
||||||
(byte)0x01,
|
(byte)0x01,
|
||||||
(byte)0x00,
|
(byte)0x00,
|
||||||
@ -56,46 +65,61 @@ public class NvHandshake {
|
|||||||
(byte)0x00
|
(byte)0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
private static void waitAndDiscardResponse(InputStream in) throws IOException
|
private static boolean waitAndDiscardResponse(InputStream in)
|
||||||
{
|
{
|
||||||
// Wait for response and discard response
|
// Wait for response and discard response
|
||||||
in.read();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(250);
|
|
||||||
} catch (InterruptedException e) { }
|
|
||||||
|
|
||||||
for (int i = 0; i < in.available(); i++)
|
|
||||||
in.read();
|
in.read();
|
||||||
|
|
||||||
|
// Wait for the full response to come in
|
||||||
|
Thread.sleep(250);
|
||||||
|
|
||||||
|
for (int i = 0; i < in.available(); i++)
|
||||||
|
in.read();
|
||||||
|
|
||||||
|
} catch (IOException e1) {
|
||||||
|
return false;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void performHandshake(String host) throws UnknownHostException, IOException
|
public static boolean performHandshake(InetAddress host) throws IOException
|
||||||
{
|
{
|
||||||
Socket s = new Socket(host, PORT);
|
Socket s = new Socket();
|
||||||
|
s.connect(new InetSocketAddress(host, PORT), HANDSHAKE_TIMEOUT);
|
||||||
|
s.setSoTimeout(HANDSHAKE_TIMEOUT);
|
||||||
OutputStream out = s.getOutputStream();
|
OutputStream out = s.getOutputStream();
|
||||||
InputStream in = s.getInputStream();
|
InputStream in = s.getInputStream();
|
||||||
|
|
||||||
// First packet
|
// First packet
|
||||||
out.write(new byte[]{0x07, 0x00, 0x00, 0x00});
|
|
||||||
out.write(PLATFORM_HELLO);
|
out.write(PLATFORM_HELLO);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
System.out.println("HS: Waiting for hello response");
|
if (!waitAndDiscardResponse(in)) {
|
||||||
|
s.close();
|
||||||
waitAndDiscardResponse(in);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Second packet
|
// Second packet
|
||||||
out.write(PACKET_2);
|
out.write(PACKET_2);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
System.out.println("HS: Waiting stage 2 response");
|
if (!waitAndDiscardResponse(in)) {
|
||||||
|
s.close();
|
||||||
waitAndDiscardResponse(in);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Third packet
|
// Third packet
|
||||||
out.write(PACKET_3);
|
out.write(PACKET_3);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
System.out.println("HS: Waiting for stage 3 response");
|
if (!waitAndDiscardResponse(in)) {
|
||||||
|
s.close();
|
||||||
waitAndDiscardResponse(in);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Fourth packet
|
// Fourth packet
|
||||||
out.write(PACKET_4);
|
out.write(PACKET_4);
|
||||||
@ -103,5 +127,7 @@ public class NvHandshake {
|
|||||||
|
|
||||||
// Done
|
// Done
|
||||||
s.close();
|
s.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@ import java.io.InputStream;
|
|||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
@ -29,13 +29,17 @@ public class NvVideoStream {
|
|||||||
public static final int RTCP_PORT = 47999;
|
public static final int RTCP_PORT = 47999;
|
||||||
public static final int FIRST_FRAME_PORT = 47996;
|
public static final int FIRST_FRAME_PORT = 47996;
|
||||||
|
|
||||||
|
public static final int FIRST_FRAME_TIMEOUT = 3000;
|
||||||
|
|
||||||
private LinkedBlockingQueue<AvRtpPacket> packets = new LinkedBlockingQueue<AvRtpPacket>();
|
private LinkedBlockingQueue<AvRtpPacket> packets = new LinkedBlockingQueue<AvRtpPacket>();
|
||||||
|
|
||||||
|
private InetAddress host;
|
||||||
private DatagramSocket rtp;
|
private DatagramSocket rtp;
|
||||||
private Socket firstFrameSocket;
|
private Socket firstFrameSocket;
|
||||||
|
|
||||||
private LinkedList<Thread> threads = new LinkedList<Thread>();
|
private LinkedList<Thread> threads = new LinkedList<Thread>();
|
||||||
|
|
||||||
|
private NvConnectionListener listener;
|
||||||
private AvVideoDepacketizer depacketizer;
|
private AvVideoDepacketizer depacketizer;
|
||||||
|
|
||||||
private DecoderRenderer decrend;
|
private DecoderRenderer decrend;
|
||||||
@ -43,9 +47,11 @@ public class NvVideoStream {
|
|||||||
|
|
||||||
private boolean aborting = false;
|
private boolean aborting = false;
|
||||||
|
|
||||||
public NvVideoStream(ConnectionStatusListener listener)
|
public NvVideoStream(InetAddress host, NvConnectionListener listener, ConnectionStatusListener avConnListener)
|
||||||
{
|
{
|
||||||
depacketizer = new AvVideoDepacketizer(listener);
|
this.host = host;
|
||||||
|
this.listener = listener;
|
||||||
|
this.depacketizer = new AvVideoDepacketizer(avConnListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void abort()
|
public void abort()
|
||||||
@ -89,14 +95,15 @@ public class NvVideoStream {
|
|||||||
threads.clear();
|
threads.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readFirstFrame(String host) throws IOException
|
private void readFirstFrame() throws IOException
|
||||||
{
|
{
|
||||||
byte[] firstFrame = new byte[1500];
|
byte[] firstFrame = new byte[1500];
|
||||||
|
|
||||||
System.out.println("VID: Waiting for first frame");
|
firstFrameSocket = new Socket();
|
||||||
firstFrameSocket = new Socket(host, FIRST_FRAME_PORT);
|
firstFrameSocket.setSoTimeout(FIRST_FRAME_TIMEOUT);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
firstFrameSocket.connect(new InetSocketAddress(host, FIRST_FRAME_PORT), FIRST_FRAME_TIMEOUT);
|
||||||
InputStream firstFrameStream = firstFrameSocket.getInputStream();
|
InputStream firstFrameStream = firstFrameSocket.getInputStream();
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
@ -110,7 +117,6 @@ public class NvVideoStream {
|
|||||||
offset += bytesRead;
|
offset += bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("VID: First frame read ("+offset+" bytes)");
|
|
||||||
depacketizer.addInputData(new AvVideoPacket(new AvByteBufferDescriptor(firstFrame, 0, offset)));
|
depacketizer.addInputData(new AvVideoPacket(new AvByteBufferDescriptor(firstFrame, 0, offset)));
|
||||||
} finally {
|
} finally {
|
||||||
firstFrameSocket.close();
|
firstFrameSocket.close();
|
||||||
@ -118,13 +124,10 @@ public class NvVideoStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupRtpSession(String host) throws SocketException, UnknownHostException
|
public void setupRtpSession() throws SocketException
|
||||||
{
|
{
|
||||||
rtp = new DatagramSocket(RTP_PORT);
|
rtp = new DatagramSocket(RTP_PORT);
|
||||||
rtp.connect(InetAddress.getByName(host), RTP_PORT);
|
rtp.connect(host, RTP_PORT);
|
||||||
rtp.setReceiveBufferSize(2097152);
|
|
||||||
System.out.println("RECV BUF: "+rtp.getReceiveBufferSize());
|
|
||||||
System.out.println("SEND BUF: "+rtp.getSendBufferSize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupDecoderRenderer(Surface renderTarget) {
|
public void setupDecoderRenderer(Surface renderTarget) {
|
||||||
@ -146,61 +149,40 @@ public class NvVideoStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startVideoStream(final String host, final Surface surface)
|
public void startVideoStream(final Surface surface) throws IOException
|
||||||
{
|
{
|
||||||
// This thread becomes the output display thread
|
// Setup the decoder and renderer
|
||||||
Thread t = new Thread() {
|
setupDecoderRenderer(surface);
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Setup the decoder and renderer
|
|
||||||
setupDecoderRenderer(surface);
|
|
||||||
|
|
||||||
// Open RTP sockets and start session
|
// Open RTP sockets and start session
|
||||||
try {
|
setupRtpSession();
|
||||||
setupRtpSession(host);
|
|
||||||
} catch (SocketException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start pinging before reading the first frame
|
// Start pinging before reading the first frame
|
||||||
// so Shield Proxy knows we're here and sends us
|
// so Shield Proxy knows we're here and sends us
|
||||||
// the reference frame
|
// the reference frame
|
||||||
startUdpPingThread();
|
startUdpPingThread();
|
||||||
|
|
||||||
// Read the first frame to start the UDP video stream
|
// Read the first frame to start the UDP video stream
|
||||||
// This MUST be called before the normal UDP receive thread
|
// This MUST be called before the normal UDP receive thread
|
||||||
// starts in order to avoid state corruption caused by two
|
// starts in order to avoid state corruption caused by two
|
||||||
// threads simultaneously adding input data.
|
// threads simultaneously adding input data.
|
||||||
try {
|
readFirstFrame();
|
||||||
readFirstFrame(host);
|
|
||||||
} catch (IOException e2) {
|
|
||||||
abort();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decrend != null) {
|
if (decrend != null) {
|
||||||
// Start the receive thread early to avoid missing
|
// Start the receive thread early to avoid missing
|
||||||
// early packets
|
// early packets
|
||||||
startReceiveThread();
|
startReceiveThread();
|
||||||
|
|
||||||
// Start the depacketizer thread to deal with the RTP data
|
// Start the depacketizer thread to deal with the RTP data
|
||||||
startDepacketizerThread();
|
startDepacketizerThread();
|
||||||
|
|
||||||
// Start decoding the data we're receiving
|
// Start decoding the data we're receiving
|
||||||
startDecoderThread();
|
startDecoderThread();
|
||||||
|
|
||||||
// Start the renderer
|
// Start the renderer
|
||||||
decrend.start();
|
decrend.start();
|
||||||
startedRendering = true;
|
startedRendering = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
threads.add(t);
|
|
||||||
t.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDecoderThread()
|
private void startDecoderThread()
|
||||||
@ -216,7 +198,7 @@ public class NvVideoStream {
|
|||||||
try {
|
try {
|
||||||
du = depacketizer.getNextDecodeUnit();
|
du = depacketizer.getNextDecodeUnit();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +224,7 @@ public class NvVideoStream {
|
|||||||
try {
|
try {
|
||||||
packet = packets.take();
|
packet = packets.take();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +251,7 @@ public class NvVideoStream {
|
|||||||
try {
|
try {
|
||||||
rtp.receive(packet);
|
rtp.receive(packet);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,14 +287,14 @@ public class NvVideoStream {
|
|||||||
try {
|
try {
|
||||||
rtp.send(pingPacket);
|
rtp.send(pingPacket);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
abort();
|
listener.connectionTerminated();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,29 @@ package com.limelight.nvstream.input;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
|
|
||||||
public class NvController {
|
public class NvController {
|
||||||
|
|
||||||
public final static int PORT = 35043;
|
public final static int PORT = 35043;
|
||||||
|
|
||||||
|
public final static int CONTROLLER_TIMEOUT = 3000;
|
||||||
|
|
||||||
|
private InetAddress host;
|
||||||
private Socket s;
|
private Socket s;
|
||||||
private OutputStream out;
|
private OutputStream out;
|
||||||
|
|
||||||
public NvController(String host) throws UnknownHostException, IOException
|
public NvController(InetAddress host)
|
||||||
{
|
{
|
||||||
s = new Socket(host, PORT);
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize() throws IOException
|
||||||
|
{
|
||||||
|
s = new Socket();
|
||||||
|
s.connect(new InetSocketAddress(host, PORT), CONTROLLER_TIMEOUT);
|
||||||
s.setTcpNoDelay(true);
|
s.setTcpNoDelay(true);
|
||||||
out = s.getOutputStream();
|
out = s.getOutputStream();
|
||||||
}
|
}
|
||||||
|
66
src/com/limelight/utils/Dialog.java
Normal file
66
src/com/limelight/utils/Dialog.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package com.limelight.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
|
||||||
|
public class Dialog implements Runnable {
|
||||||
|
private String title, message;
|
||||||
|
private Activity activity;
|
||||||
|
private boolean endAfterDismiss;
|
||||||
|
|
||||||
|
AlertDialog alert;
|
||||||
|
|
||||||
|
private static ArrayList<Dialog> rundownDialogs = new ArrayList<Dialog>();
|
||||||
|
|
||||||
|
public Dialog(Activity activity, String title, String message, boolean endAfterDismiss)
|
||||||
|
{
|
||||||
|
this.activity = activity;
|
||||||
|
this.title = title;
|
||||||
|
this.message = message;
|
||||||
|
this.endAfterDismiss = endAfterDismiss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void closeDialogs()
|
||||||
|
{
|
||||||
|
for (Dialog d : rundownDialogs)
|
||||||
|
d.alert.dismiss();
|
||||||
|
|
||||||
|
rundownDialogs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void displayDialog(Activity activity, String title, String message, boolean endAfterDismiss)
|
||||||
|
{
|
||||||
|
activity.runOnUiThread(new Dialog(activity, title, message, endAfterDismiss));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// If we're dying, don't bother creating a dialog
|
||||||
|
if (activity.isFinishing())
|
||||||
|
return;
|
||||||
|
|
||||||
|
alert = new AlertDialog.Builder(activity).create();
|
||||||
|
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setMessage(message);
|
||||||
|
alert.setCancelable(false);
|
||||||
|
alert.setCanceledOnTouchOutside(false);
|
||||||
|
|
||||||
|
alert.setButton(AlertDialog.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
alert.dismiss();
|
||||||
|
rundownDialogs.remove(this);
|
||||||
|
|
||||||
|
if (endAfterDismiss)
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
rundownDialogs.add(this);
|
||||||
|
alert.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
98
src/com/limelight/utils/SpinnerDialog.java
Normal file
98
src/com/limelight/utils/SpinnerDialog.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package com.limelight.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.DialogInterface.OnCancelListener;
|
||||||
|
|
||||||
|
public class SpinnerDialog implements Runnable,OnCancelListener {
|
||||||
|
private String title, message;
|
||||||
|
private Activity activity;
|
||||||
|
private ProgressDialog progress;
|
||||||
|
private boolean finish;
|
||||||
|
|
||||||
|
private static ArrayList<SpinnerDialog> rundownDialogs = new ArrayList<SpinnerDialog>();
|
||||||
|
|
||||||
|
public SpinnerDialog(Activity activity, String title, String message, boolean finish)
|
||||||
|
{
|
||||||
|
this.activity = activity;
|
||||||
|
this.title = title;
|
||||||
|
this.message = message;
|
||||||
|
this.progress = null;
|
||||||
|
this.finish = finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpinnerDialog displayDialog(Activity activity, String title, String message, boolean finish)
|
||||||
|
{
|
||||||
|
SpinnerDialog spinner = new SpinnerDialog(activity, title, message, finish);
|
||||||
|
activity.runOnUiThread(spinner);
|
||||||
|
return spinner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void closeDialogs()
|
||||||
|
{
|
||||||
|
for (SpinnerDialog d : rundownDialogs)
|
||||||
|
d.progress.dismiss();
|
||||||
|
|
||||||
|
rundownDialogs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dismiss()
|
||||||
|
{
|
||||||
|
// Running again with progress != null will destroy it
|
||||||
|
activity.runOnUiThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(final String message)
|
||||||
|
{
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progress.setMessage(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
if (progress == null)
|
||||||
|
{
|
||||||
|
// If we're dying, don't bother creating a dialog
|
||||||
|
if (activity.isFinishing())
|
||||||
|
return;
|
||||||
|
|
||||||
|
progress = new ProgressDialog(activity);
|
||||||
|
|
||||||
|
progress.setTitle(title);
|
||||||
|
progress.setMessage(message);
|
||||||
|
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||||
|
progress.setOnCancelListener(this);
|
||||||
|
|
||||||
|
// If we want to finish the activity when this is killed, make it cancellable
|
||||||
|
if (finish)
|
||||||
|
{
|
||||||
|
progress.setCancelable(true);
|
||||||
|
progress.setCanceledOnTouchOutside(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progress.setCancelable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progress.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
// This will only be called if finish was true, so we don't need to check again
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user