Add UI indications of the connection status and failure

This commit is contained in:
Cameron Gutman 2013-11-23 01:03:37 -05:00
parent c88d23001e
commit 4d5849f448
13 changed files with 569 additions and 244 deletions

View File

@ -2,7 +2,9 @@ package com.limelight;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import org.xmlpull.v1.XmlPullParserException;
@ -88,26 +90,30 @@ public class Connection extends Activity {
return;
}
NvHTTP httpConn = new NvHTTP(hostText.getText().toString(), macAddress);
NvHTTP httpConn;
String message;
try {
if (httpConn.getPairState()) {
message = "Already paired";
}
else {
int session = httpConn.getSessionId();
if (session == 0) {
message = "Pairing was declined by the target";
httpConn = new NvHTTP(InetAddress.getByName(hostText.getText().toString()), macAddress);
try {
if (httpConn.getPairState()) {
message = "Already paired";
}
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) {
message = e.getMessage();
} catch (XmlPullParserException e) {
message = e.getMessage();
} catch (UnknownHostException e1) {
message = "Failed to resolve host";
}
final String toastMessage = message;

View File

@ -1,7 +1,10 @@
package com.limelight;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.NvConnectionListener;
import com.limelight.nvstream.input.NvControllerPacket;
import com.limelight.utils.Dialog;
import com.limelight.utils.SpinnerDialog;
import android.app.Activity;
import android.graphics.PixelFormat;
@ -18,7 +21,7 @@ import android.view.Window;
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 byte leftTrigger = 0x00;
private byte rightTrigger = 0x00;
@ -33,6 +36,8 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
private boolean hasMoved = false;
private NvConnection conn;
private SpinnerDialog spinner;
private boolean displayedFailureDialog = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -59,6 +64,9 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
sh.setFixedSize(1280, 720);
sh.setFormat(PixelFormat.RGBX_8888);
// Start the spinner
spinner = SpinnerDialog.displayDialog(this, "Establishing Connection", "Starting connection", true);
// Start the connection
conn = new NvConnection(Game.this.getIntent().getStringExtra("host"), Game.this, sv.getHolder().getSurface());
conn.start();
@ -94,6 +102,13 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
super.onPause();
}
@Override
protected void onDestroy() {
SpinnerDialog.closeDialogs();
Dialog.closeDialogs();
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
@ -439,4 +454,42 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
// Send it to the activity's touch event handler
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;
}
}

View File

@ -5,7 +5,6 @@ import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;
@ -35,6 +34,15 @@ public class NvAudioStream {
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()
{
if (aborting) {
@ -66,40 +74,25 @@ public class NvAudioStream {
threads.clear();
}
public void startAudioStream(final String host)
{
new Thread(new Runnable() {
@Override
public void run() {
try {
setupRtpSession(host);
} catch (SocketException e) {
e.printStackTrace();
return;
} catch (UnknownHostException e) {
e.printStackTrace();
return;
}
setupAudio();
startReceiveThread();
startDepacketizerThread();
startDecoderThread();
startUdpPingThread();
}
}).start();
public void startAudioStream() throws SocketException
{
setupRtpSession();
setupAudio();
startReceiveThread();
startDepacketizerThread();
startDecoderThread();
startUdpPingThread();
}
private void setupRtpSession(String host) throws SocketException, UnknownHostException
private void setupRtpSession() throws SocketException
{
rtp = new DatagramSocket(RTP_PORT);
rtp.connect(InetAddress.getByName(host), RTP_PORT);
rtp.connect(host, RTP_PORT);
}
private void setupAudio()
@ -108,12 +101,8 @@ public class NvAudioStream {
int err;
err = OpusDecoder.init();
if (err == 0) {
System.out.println("Opus decoder initialized");
}
else {
System.err.println("Opus decoder init failed: "+err);
return;
if (err != 0) {
throw new IllegalStateException("Opus decoder failed to initialize");
}
switch (OpusDecoder.getChannelCount())
@ -125,8 +114,7 @@ public class NvAudioStream {
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
default:
System.err.println("Unsupported channel count");
return;
throw new IllegalStateException("Opus decoder returned unhandled channel count");
}
track = new AudioTrack(AudioManager.STREAM_MUSIC,
@ -153,7 +141,7 @@ public class NvAudioStream {
try {
packet = packets.take();
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
@ -178,7 +166,7 @@ public class NvAudioStream {
try {
samples = depacketizer.getNextDecodedData();
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
@ -204,7 +192,7 @@ public class NvAudioStream {
try {
rtp.receive(packet);
} catch (IOException e) {
abort();
listener.connectionTerminated();
return;
}
@ -240,14 +228,14 @@ public class NvAudioStream {
try {
rtp.send(pingPacket);
} catch (IOException e) {
abort();
listener.connectionTerminated();
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
}

View File

@ -38,10 +38,10 @@ public class NvComputer {
this.uniqueID = uniqueID;
try {
this.nvHTTP = new NvHTTP(this.ipAddressString, NvConnection.getMacAddressString());
this.nvHTTP = new NvHTTP(this.ipAddress, NvConnection.getMacAddressString());
} catch (SocketException e) {
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();

View File

@ -23,18 +23,21 @@ import com.limelight.nvstream.input.NvController;
public class NvConnection {
private String host;
private Game activity;
private NvConnectionListener listener;
private InetAddress hostAddr;
private NvControl controlStream;
private NvController inputStream;
private Surface video;
private NvVideoStream videoStream;
private NvAudioStream audioStream = new NvAudioStream();
private NvAudioStream audioStream;
private ThreadPoolExecutor threadPool;
public NvConnection(String host, Game activity, Surface video)
{
this.host = host;
this.listener = activity;
this.activity = activity;
this.video = video;
this.threadPool = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());
@ -116,6 +119,106 @@ public class NvConnection {
inputStream = null;
}
}
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()
{
@ -125,31 +228,16 @@ public class NvConnection {
checkDataConnection();
try {
host = InetAddress.getByName(host).getHostAddress();
hostAddr = InetAddress.getByName(host);
} catch (UnknownHostException e) {
e.printStackTrace();
displayToast(e.getMessage());
listener.connectionTerminated();
return;
}
try {
startSteamBigPicture();
performHandshake();
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();
}
establishConnection();
activity.hideSystemUi();
}
}).start();
}
@ -173,8 +261,7 @@ public class NvConnection {
try {
inputStream.sendMouseMove(deltaX, deltaY);
} catch (IOException e) {
displayToast(e.getMessage());
NvConnection.this.stop();
listener.connectionTerminated();
}
}
});
@ -191,8 +278,7 @@ public class NvConnection {
try {
inputStream.sendMouseButtonDown();
} catch (IOException e) {
displayToast(e.getMessage());
NvConnection.this.stop();
listener.connectionTerminated();
}
}
});
@ -209,8 +295,7 @@ public class NvConnection {
try {
inputStream.sendMouseButtonUp();
} catch (IOException e) {
displayToast(e.getMessage());
NvConnection.this.stop();
listener.connectionTerminated();
}
}
});
@ -232,8 +317,7 @@ public class NvConnection {
rightTrigger, leftStickX, leftStickY,
rightStickX, rightStickY);
} catch (IOException e) {
displayToast(e.getMessage());
NvConnection.this.stop();
listener.connectionTerminated();
}
}
});
@ -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);
}
}

View 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();
}

View File

@ -3,8 +3,9 @@ package com.limelight.nvstream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -14,6 +15,8 @@ public class NvControl implements ConnectionStatusListener {
public static final int PORT = 47995;
public static final int CONTROL_TIMEOUT = 3000;
public static final short PTYPE_HELLO = 0x1204;
public static final short PPAYLEN_HELLO = 0x0004;
public static final byte[] PPAYLOAD_HELLO =
@ -140,6 +143,9 @@ public class NvControl implements ConnectionStatusListener {
private int seqNum;
private NvConnectionListener listener;
private InetAddress host;
private Socket s;
private InputStream in;
private OutputStream out;
@ -148,20 +154,28 @@ public class NvControl implements ConnectionStatusListener {
private Thread jitterThread;
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();
out = s.getOutputStream();
}
public void sendPacket(NvCtlPacket packet) throws IOException
private void sendPacket(NvCtlPacket packet) throws IOException
{
out.write(packet.toWire());
out.flush();
}
public NvControl.NvCtlResponse sendAndGetReply(NvCtlPacket packet) throws IOException
private NvControl.NvCtlResponse sendAndGetReply(NvCtlPacket packet) throws IOException
{
sendPacket(packet);
return new NvCtlResponse(in);
@ -208,15 +222,10 @@ public class NvControl implements ConnectionStatusListener {
public void start() throws IOException
{
System.out.println("CTL: Sending hello");
sendHello();
System.out.println("CTL: Sending config");
sendConfig();
System.out.println("CTL: Initial ping/pong");
pingPong();
System.out.println("CTL: Sending and waiting for 1405");
send1405AndGetResponse();
System.out.println("CTL: Launching heartbeat thread");
heartbeatThread = new Thread() {
@Override
@ -226,7 +235,7 @@ public class NvControl implements ConnectionStatusListener {
try {
sendHeartbeat();
} catch (IOException e1) {
abort();
listener.connectionTerminated();
return;
}
@ -234,7 +243,7 @@ public class NvControl implements ConnectionStatusListener {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
}
@ -253,14 +262,14 @@ public class NvControl implements ConnectionStatusListener {
try {
sendJitter();
} catch (IOException e1) {
abort();
listener.connectionTerminated();
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
}

View File

@ -3,7 +3,9 @@ package com.limelight.nvstream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.util.LinkedList;
import java.util.Stack;
@ -15,11 +17,16 @@ public class NvHTTP {
private String macAddress;
public static final int PORT = 47989;
public static final int CONNECTION_TIMEOUT = 3000;
public static final int REQUEST_TIMEOUT = 15000;
public String baseUrl;
public NvHTTP(String host, String macAddress) {
public NvHTTP(InetAddress host, String macAddress) {
this.macAddress = macAddress;
this.baseUrl = "http://" + host + ":" + PORT;
this.baseUrl = "http://" + host.getHostAddress() + ":" + PORT;
}
private String getXmlString(InputStream in, String tagname)
@ -53,7 +60,12 @@ public class NvHTTP {
}
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 {

View File

@ -3,15 +3,23 @@ package com.limelight.nvstream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;;
public class NvHandshake {
public static final int PORT = 47991;
// android
public static final int HANDSHAKE_TIMEOUT = 3000;
public static final byte[] PLATFORM_HELLO =
{
(byte)0x07,
(byte)0x00,
(byte)0x00,
(byte)0x00,
// android in ASCII
(byte)0x61,
(byte)0x6e,
(byte)0x64,
@ -19,6 +27,7 @@ public class NvHandshake {
(byte)0x6f,
(byte)0x69,
(byte)0x64,
(byte)0x03,
(byte)0x01,
(byte)0x00,
@ -56,46 +65,61 @@ public class NvHandshake {
(byte)0x00
};
private static void waitAndDiscardResponse(InputStream in) throws IOException
private static boolean waitAndDiscardResponse(InputStream in)
{
// Wait for response and discard response
in.read();
try {
Thread.sleep(250);
} catch (InterruptedException e) { }
for (int i = 0; i < in.available(); i++)
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();
InputStream in = s.getInputStream();
// First packet
out.write(new byte[]{0x07, 0x00, 0x00, 0x00});
out.write(PLATFORM_HELLO);
out.flush();
System.out.println("HS: Waiting for hello response");
waitAndDiscardResponse(in);
if (!waitAndDiscardResponse(in)) {
s.close();
return false;
}
// Second packet
out.write(PACKET_2);
System.out.println("HS: Waiting stage 2 response");
waitAndDiscardResponse(in);
out.flush();
if (!waitAndDiscardResponse(in)) {
s.close();
return false;
}
// Third packet
out.write(PACKET_3);
out.flush();
System.out.println("HS: Waiting for stage 3 response");
waitAndDiscardResponse(in);
if (!waitAndDiscardResponse(in)) {
s.close();
return false;
}
// Fourth packet
out.write(PACKET_4);
@ -103,5 +127,7 @@ public class NvHandshake {
// Done
s.close();
return true;
}
}

View File

@ -5,9 +5,9 @@ import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;
@ -29,13 +29,17 @@ public class NvVideoStream {
public static final int RTCP_PORT = 47999;
public static final int FIRST_FRAME_PORT = 47996;
public static final int FIRST_FRAME_TIMEOUT = 3000;
private LinkedBlockingQueue<AvRtpPacket> packets = new LinkedBlockingQueue<AvRtpPacket>();
private InetAddress host;
private DatagramSocket rtp;
private Socket firstFrameSocket;
private LinkedList<Thread> threads = new LinkedList<Thread>();
private NvConnectionListener listener;
private AvVideoDepacketizer depacketizer;
private DecoderRenderer decrend;
@ -43,9 +47,11 @@ public class NvVideoStream {
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()
@ -89,14 +95,15 @@ public class NvVideoStream {
threads.clear();
}
private void readFirstFrame(String host) throws IOException
private void readFirstFrame() throws IOException
{
byte[] firstFrame = new byte[1500];
System.out.println("VID: Waiting for first frame");
firstFrameSocket = new Socket(host, FIRST_FRAME_PORT);
firstFrameSocket = new Socket();
firstFrameSocket.setSoTimeout(FIRST_FRAME_TIMEOUT);
try {
firstFrameSocket.connect(new InetSocketAddress(host, FIRST_FRAME_PORT), FIRST_FRAME_TIMEOUT);
InputStream firstFrameStream = firstFrameSocket.getInputStream();
int offset = 0;
@ -110,7 +117,6 @@ public class NvVideoStream {
offset += bytesRead;
}
System.out.println("VID: First frame read ("+offset+" bytes)");
depacketizer.addInputData(new AvVideoPacket(new AvByteBufferDescriptor(firstFrame, 0, offset)));
} finally {
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.connect(InetAddress.getByName(host), RTP_PORT);
rtp.setReceiveBufferSize(2097152);
System.out.println("RECV BUF: "+rtp.getReceiveBufferSize());
System.out.println("SEND BUF: "+rtp.getSendBufferSize());
rtp.connect(host, RTP_PORT);
}
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
Thread t = new Thread() {
@Override
public void run() {
// Setup the decoder and renderer
setupDecoderRenderer(surface);
// Open RTP sockets and start session
try {
setupRtpSession(host);
} catch (SocketException e) {
e.printStackTrace();
return;
} catch (UnknownHostException e) {
e.printStackTrace();
return;
}
// Start pinging before reading the first frame
// so Shield Proxy knows we're here and sends us
// the reference frame
startUdpPingThread();
// Read the first frame to start the UDP video stream
// This MUST be called before the normal UDP receive thread
// starts in order to avoid state corruption caused by two
// threads simultaneously adding input data.
try {
readFirstFrame(host);
} catch (IOException e2) {
abort();
return;
}
if (decrend != null) {
// Start the receive thread early to avoid missing
// early packets
startReceiveThread();
// Start the depacketizer thread to deal with the RTP data
startDepacketizerThread();
// Start decoding the data we're receiving
startDecoderThread();
// Start the renderer
decrend.start();
startedRendering = true;
}
}
};
threads.add(t);
t.start();
// Setup the decoder and renderer
setupDecoderRenderer(surface);
// Open RTP sockets and start session
setupRtpSession();
// Start pinging before reading the first frame
// so Shield Proxy knows we're here and sends us
// the reference frame
startUdpPingThread();
// Read the first frame to start the UDP video stream
// This MUST be called before the normal UDP receive thread
// starts in order to avoid state corruption caused by two
// threads simultaneously adding input data.
readFirstFrame();
if (decrend != null) {
// Start the receive thread early to avoid missing
// early packets
startReceiveThread();
// Start the depacketizer thread to deal with the RTP data
startDepacketizerThread();
// Start decoding the data we're receiving
startDecoderThread();
// Start the renderer
decrend.start();
startedRendering = true;
}
}
private void startDecoderThread()
@ -216,7 +198,7 @@ public class NvVideoStream {
try {
du = depacketizer.getNextDecodeUnit();
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
@ -242,7 +224,7 @@ public class NvVideoStream {
try {
packet = packets.take();
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
@ -269,7 +251,7 @@ public class NvVideoStream {
try {
rtp.receive(packet);
} catch (IOException e) {
abort();
listener.connectionTerminated();
return;
}
@ -305,14 +287,14 @@ public class NvVideoStream {
try {
rtp.send(pingPacket);
} catch (IOException e) {
abort();
listener.connectionTerminated();
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
abort();
listener.connectionTerminated();
return;
}
}

View File

@ -2,19 +2,29 @@ package com.limelight.nvstream.input;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class NvController {
public final static int PORT = 35043;
public final static int CONTROLLER_TIMEOUT = 3000;
private InetAddress host;
private Socket s;
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);
out = s.getOutputStream();
}

View 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();
}
}

View 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();
}
}