mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-04-03 06:26:16 +00:00
Add implementation of http negotiation, handshake, control, and input packets
This commit is contained in:
30
src/com/limelight/Game.java
Normal file
30
src/com/limelight/Game.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.limelight;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.widget.MediaController;
|
||||
import android.widget.VideoView;
|
||||
|
||||
|
||||
public class Game extends Activity {
|
||||
private VideoView vv;
|
||||
private MediaController mc;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game);
|
||||
|
||||
vv = (VideoView) findViewById(R.id.videoView);
|
||||
|
||||
mc = new MediaController(this);
|
||||
mc.setAnchorView(vv);
|
||||
|
||||
Uri video = Uri.parse("rtsp://141.213.191.236:47902/nvstream");
|
||||
vv.setMediaController(mc);
|
||||
vv.setVideoURI(video);
|
||||
|
||||
vv.start();
|
||||
}
|
||||
}
|
||||
73
src/com/limelight/nvstream/NvAudioStream.java
Normal file
73
src/com/limelight/nvstream/NvAudioStream.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package com.limelight.nvstream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import android.net.rtp.AudioCodec;
|
||||
import android.net.rtp.AudioGroup;
|
||||
import android.net.rtp.AudioStream;
|
||||
|
||||
public class NvAudioStream {
|
||||
private AudioGroup group;
|
||||
private AudioStream stream;
|
||||
|
||||
public static final int PORT = 48000;
|
||||
|
||||
/*public void startStream(String host) throws SocketException, UnknownHostException
|
||||
{
|
||||
System.out.println("Starting audio group");
|
||||
group = new AudioGroup();
|
||||
group.setMode(AudioGroup.MODE_NORMAL);
|
||||
|
||||
System.out.println("Starting audio stream");
|
||||
stream = new AudioStream(InetAddress.getByAddress(new byte[]{0,0,0,0}));
|
||||
stream.setMode(AudioStream.MODE_NORMAL);
|
||||
stream.associate(InetAddress.getByName(host), PORT);
|
||||
stream.setCodec(AudioCodec.PCMA);
|
||||
stream.join(group);
|
||||
|
||||
for (AudioCodec c : AudioCodec.getCodecs())
|
||||
System.out.println(c.type + " " + c.fmtp + " " + c.rtpmap);
|
||||
|
||||
System.out.println("Joined");
|
||||
}*/
|
||||
|
||||
public void start()
|
||||
{
|
||||
new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
DatagramSocket ds;
|
||||
try {
|
||||
ds = new DatagramSocket(PORT);
|
||||
} catch (SocketException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
||||
DatagramPacket dp = new DatagramPacket(new byte[1500], 1500);
|
||||
|
||||
try {
|
||||
ds.receive(dp);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
|
||||
//System.out.println("Got UDP 48000: "+dp.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@@ -12,20 +12,67 @@ public class NvConnection {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
private void delay(int ms)
|
||||
{
|
||||
try {
|
||||
Thread.sleep(ms);
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void doShit() throws XmlPullParserException, IOException
|
||||
{
|
||||
NvHttp h = new NvHttp(host, "b0:ee:45:57:5d:5f");
|
||||
|
||||
System.out.println("Shield Shit");
|
||||
System.out.println("Begin Shield Action");
|
||||
System.out.println(h.getAppVersion());
|
||||
System.out.println(h.getPairState());
|
||||
|
||||
|
||||
int sessionId = h.getSessionId();
|
||||
System.out.println("Session ID: "+sessionId);
|
||||
int appId = h.getSteamAppId(sessionId);
|
||||
System.out.println("Steam app ID: "+appId);
|
||||
int gameSession = h.launchApp(sessionId, appId);
|
||||
System.out.println("Started game session: "+gameSession);
|
||||
|
||||
System.out.println("Starting handshake");
|
||||
NvHandshake.performHandshake(host);
|
||||
System.out.println("Handshake complete");
|
||||
|
||||
NvControl nvC = new NvControl(host);
|
||||
|
||||
System.out.println("Starting control");
|
||||
nvC.beginControl();
|
||||
|
||||
System.out.println("Startup controller");
|
||||
NvController controller = new NvController(host);
|
||||
|
||||
// Wait 3 seconds to start input
|
||||
delay(3000);
|
||||
|
||||
System.out.println("Beginning controller input");
|
||||
controller.sendLeftButton();
|
||||
delay(100);
|
||||
controller.clearButtons();
|
||||
delay(250);
|
||||
controller.sendRightButton();
|
||||
delay(100);
|
||||
controller.clearButtons();
|
||||
delay(250);
|
||||
controller.sendRightButton();
|
||||
delay(100);
|
||||
controller.clearButtons();
|
||||
delay(250);
|
||||
controller.sendRightButton();
|
||||
delay(100);
|
||||
controller.clearButtons();
|
||||
delay(250);
|
||||
controller.sendLeftButton();
|
||||
delay(100);
|
||||
controller.clearButtons();
|
||||
|
||||
new NvAudioStream().start();
|
||||
new NvVideoStream().start(host);
|
||||
}
|
||||
}
|
||||
|
||||
384
src/com/limelight/nvstream/NvControl.java
Normal file
384
src/com/limelight/nvstream/NvControl.java
Normal file
@@ -0,0 +1,384 @@
|
||||
package com.limelight.nvstream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class NvControl {
|
||||
|
||||
public static final int PORT = 47995;
|
||||
|
||||
public static final short PTYPE_1 = 0x1204;
|
||||
public static final short PPAYLEN_1 = 0x0004;
|
||||
public static final byte[] PPAYLOAD_1 =
|
||||
{
|
||||
(byte)0x00,
|
||||
(byte)0x05,
|
||||
(byte)0x00,
|
||||
(byte)0x00
|
||||
};
|
||||
|
||||
public static final short PTYPE_KEEPALIVE = 0x13ff;
|
||||
public static final short PPAYLEN_KEEPALIVE = 0x0000;
|
||||
|
||||
public static final short PTYPE_HEARTBEAT = 0x1401;
|
||||
public static final short PPAYLEN_HEARTBEAT = 0x0000;
|
||||
|
||||
public static final short PTYPE_1405 = 0x1405;
|
||||
public static final short PPAYLEN_1405 = 0x0000;
|
||||
|
||||
public static final short PTYPE_1404 = 0x1404;
|
||||
public static final short PPAYLEN_1404 = 0x0010;
|
||||
public static final byte[] PPAYLOAD_1404 = new byte[]
|
||||
{
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
|
||||
public static final short PTYPE_CONFIG = 0x1205;
|
||||
public static final short PPAYLEN_CONFIG = 0x0004;
|
||||
public static final int[] PPAYLOAD_CONFIG =
|
||||
{
|
||||
720,
|
||||
266758,
|
||||
1,
|
||||
266762,
|
||||
30,
|
||||
70151,
|
||||
68291329,
|
||||
1280,
|
||||
68291584,
|
||||
1280,
|
||||
68291840,
|
||||
15360,
|
||||
68292096,
|
||||
25600,
|
||||
68292352,
|
||||
2048,
|
||||
68292608,
|
||||
1024,
|
||||
68289024,
|
||||
262144,
|
||||
17957632,
|
||||
302055424,
|
||||
134217729,
|
||||
16777490,
|
||||
70153,
|
||||
68293120,
|
||||
768000,
|
||||
17961216,
|
||||
303235072,
|
||||
335609857,
|
||||
838861842,
|
||||
352321536,
|
||||
1006634002,
|
||||
369098752,
|
||||
335545362,
|
||||
385875968,
|
||||
1042,
|
||||
402653184,
|
||||
134218770,
|
||||
419430400,
|
||||
167773203,
|
||||
436207616,
|
||||
855638290,
|
||||
266779,
|
||||
7000,
|
||||
266780,
|
||||
2000,
|
||||
266781,
|
||||
50,
|
||||
266782,
|
||||
3000,
|
||||
266783,
|
||||
2,
|
||||
266794,
|
||||
5000,
|
||||
266795,
|
||||
500,
|
||||
266784,
|
||||
75,
|
||||
266785,
|
||||
25,
|
||||
266786,
|
||||
10,
|
||||
266787,
|
||||
60,
|
||||
266788,
|
||||
30,
|
||||
266789,
|
||||
3,
|
||||
266790,
|
||||
1000,
|
||||
266791,
|
||||
5000,
|
||||
266792,
|
||||
5000,
|
||||
266793,
|
||||
5000,
|
||||
70190,
|
||||
68301063,
|
||||
10240,
|
||||
68301312,
|
||||
6400,
|
||||
68301568,
|
||||
768000,
|
||||
68299776,
|
||||
768,
|
||||
68300032,
|
||||
2560,
|
||||
68300544,
|
||||
0,
|
||||
34746368,
|
||||
(int)0xFE000000
|
||||
};
|
||||
|
||||
public static final short PTYPE_HELLO = 0x1204;
|
||||
public static final short PPAYLEN_HELLO = 0x0004;
|
||||
public static final byte[] PPAYLOAD_HELLO = new byte[] {
|
||||
0x04, 0x00, 0x00, 0x05
|
||||
};
|
||||
|
||||
private Socket s;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
|
||||
public NvControl(String host) throws UnknownHostException, IOException
|
||||
{
|
||||
s = new Socket(host, PORT);
|
||||
in = s.getInputStream();
|
||||
out = s.getOutputStream();
|
||||
}
|
||||
|
||||
public void sendPacket(NvCtlPacket packet) throws IOException
|
||||
{
|
||||
out.write(packet.toWire());
|
||||
out.flush();
|
||||
}
|
||||
public NvControl.NvCtlResponse sendAndGetReply(NvCtlPacket packet) throws IOException
|
||||
{
|
||||
sendPacket(packet);
|
||||
return new NvCtlResponse(in);
|
||||
}
|
||||
|
||||
public void beginControl() 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: Sending 1404");
|
||||
//send1404();
|
||||
System.out.println("CTL: Launching heartbeat thread");
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (;;)
|
||||
{
|
||||
try {
|
||||
sendHeartbeat();
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void endControl() throws IOException
|
||||
{
|
||||
s.close();
|
||||
}
|
||||
|
||||
private NvControl.NvCtlResponse send1405AndGetResponse() throws IOException
|
||||
{
|
||||
return sendAndGetReply(new NvCtlPacket(PTYPE_1405, PPAYLEN_1405));
|
||||
}
|
||||
|
||||
private void send1404() throws IOException
|
||||
{
|
||||
sendPacket(new NvCtlPacket(PTYPE_1404, PPAYLEN_1404));
|
||||
}
|
||||
|
||||
private void sendHello() throws IOException
|
||||
{
|
||||
sendPacket(new NvCtlPacket(PTYPE_HELLO, PPAYLEN_HELLO, PPAYLOAD_HELLO));
|
||||
}
|
||||
|
||||
private void sendConfig() throws IOException
|
||||
{
|
||||
ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLOAD_CONFIG.length * 4 + 3]).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i : PPAYLOAD_CONFIG)
|
||||
conf.putInt(i);
|
||||
|
||||
conf.putShort((short)0x0013);
|
||||
conf.put((byte) 0x00);
|
||||
|
||||
sendPacket(new NvCtlPacket(PTYPE_CONFIG, PPAYLEN_CONFIG, conf.array()));
|
||||
}
|
||||
|
||||
private void sendHeartbeat() throws IOException
|
||||
{
|
||||
sendPacket(new NvCtlPacket(PTYPE_HEARTBEAT, PPAYLEN_HEARTBEAT));
|
||||
}
|
||||
|
||||
private NvControl.NvCtlResponse pingPong() throws IOException
|
||||
{
|
||||
sendPacket(new NvCtlPacket(PTYPE_KEEPALIVE, PPAYLEN_KEEPALIVE));
|
||||
return new NvControl.NvCtlResponse(in);
|
||||
}
|
||||
|
||||
class NvCtlPacket {
|
||||
public short type;
|
||||
public short paylen;
|
||||
public byte[] payload;
|
||||
|
||||
public NvCtlPacket(InputStream in) throws IOException
|
||||
{
|
||||
byte[] header = new byte[4];
|
||||
|
||||
int offset = 0;
|
||||
do
|
||||
{
|
||||
offset = in.read(header, offset, header.length - offset);
|
||||
} while (offset != header.length);
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
type = bb.getShort();
|
||||
paylen = bb.getShort();
|
||||
|
||||
if (paylen != 0)
|
||||
{
|
||||
payload = new byte[paylen];
|
||||
|
||||
offset = 0;
|
||||
do
|
||||
{
|
||||
offset = in.read(payload, offset, payload.length - offset);
|
||||
} while (offset != payload.length);
|
||||
}
|
||||
}
|
||||
|
||||
public NvCtlPacket(byte[] payload)
|
||||
{
|
||||
ByteBuffer bb = ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
type = bb.getShort();
|
||||
paylen = bb.getShort();
|
||||
|
||||
if (bb.hasRemaining())
|
||||
{
|
||||
payload = new byte[bb.remaining()];
|
||||
bb.get(payload);
|
||||
}
|
||||
}
|
||||
|
||||
public NvCtlPacket(short type, short paylen)
|
||||
{
|
||||
this.type = type;
|
||||
this.paylen = paylen;
|
||||
}
|
||||
|
||||
public NvCtlPacket(short type, short paylen, byte[] payload)
|
||||
{
|
||||
this.type = type;
|
||||
this.paylen = paylen;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public short getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public short getPaylen()
|
||||
{
|
||||
return paylen;
|
||||
}
|
||||
|
||||
public void setType(short type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setPaylen(short paylen)
|
||||
{
|
||||
this.paylen = paylen;
|
||||
}
|
||||
|
||||
public byte[] toWire()
|
||||
{
|
||||
ByteBuffer bb = ByteBuffer.allocate(4 + (payload != null ? payload.length : 0)).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
bb.putShort(type);
|
||||
bb.putShort(paylen);
|
||||
|
||||
if (payload != null)
|
||||
bb.put(payload);
|
||||
|
||||
return bb.array();
|
||||
}
|
||||
}
|
||||
|
||||
class NvCtlResponse extends NvCtlPacket {
|
||||
public short status;
|
||||
|
||||
public NvCtlResponse(InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public NvCtlResponse(short type, short paylen) {
|
||||
super(type, paylen);
|
||||
}
|
||||
|
||||
public NvCtlResponse(short type, short paylen, byte[] payload) {
|
||||
super(type, paylen, payload);
|
||||
}
|
||||
|
||||
public NvCtlResponse(byte[] payload) {
|
||||
super(payload);
|
||||
}
|
||||
|
||||
public void setStatusCode(short status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public short getStatusCode()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/com/limelight/nvstream/NvController.java
Normal file
40
src/com/limelight/nvstream/NvController.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.limelight.nvstream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class NvController {
|
||||
|
||||
public final static int PORT = 35043;
|
||||
|
||||
private Socket s;
|
||||
private OutputStream out;
|
||||
|
||||
public NvController(String host) throws UnknownHostException, IOException
|
||||
{
|
||||
s = new Socket(host, PORT);
|
||||
out = s.getOutputStream();
|
||||
}
|
||||
|
||||
// Example
|
||||
public void sendLeftButton() throws IOException
|
||||
{
|
||||
out.write(new NvInputPacket(NvInputPacket.LEFT_FLAG, (byte)0, (byte)0, (short)0, (short)0).toWire());
|
||||
}
|
||||
|
||||
// Example
|
||||
public void sendRightButton() throws IOException
|
||||
{
|
||||
out.write(new NvInputPacket(NvInputPacket.RIGHT_FLAG, (byte)0, (byte)0, (short)0, (short)0).toWire());
|
||||
}
|
||||
|
||||
// Example
|
||||
public void clearButtons() throws IOException
|
||||
{
|
||||
out.write(new NvInputPacket((short)0, (byte)0, (byte)0, (short)0, (short)0).toWire());
|
||||
}
|
||||
}
|
||||
107
src/com/limelight/nvstream/NvHandshake.java
Normal file
107
src/com/limelight/nvstream/NvHandshake.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.limelight.nvstream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;;
|
||||
|
||||
public class NvHandshake {
|
||||
public static final int PORT = 47991;
|
||||
|
||||
// android
|
||||
public static final byte[] PLATFORM_HELLO =
|
||||
{
|
||||
(byte)0x61,
|
||||
(byte)0x6e,
|
||||
(byte)0x64,
|
||||
(byte)0x72,
|
||||
(byte)0x6f,
|
||||
(byte)0x69,
|
||||
(byte)0x64,
|
||||
(byte)0x03,
|
||||
(byte)0x01,
|
||||
(byte)0x00,
|
||||
(byte)0x00
|
||||
};
|
||||
|
||||
public static final byte[] PACKET_2 =
|
||||
{
|
||||
(byte)0x01,
|
||||
(byte)0x03,
|
||||
(byte)0x02,
|
||||
(byte)0x00,
|
||||
(byte)0x08,
|
||||
(byte)0x00
|
||||
};
|
||||
|
||||
public static final byte[] PACKET_3 =
|
||||
{
|
||||
(byte)0x04,
|
||||
(byte)0x01,
|
||||
(byte)0x00,
|
||||
(byte)0x00,
|
||||
|
||||
(byte)0x00,
|
||||
(byte)0x00,
|
||||
(byte)0x00,
|
||||
(byte)0x00
|
||||
};
|
||||
|
||||
public static final byte[] PACKET_4 =
|
||||
{
|
||||
(byte)0x01,
|
||||
(byte)0x01,
|
||||
(byte)0x00,
|
||||
(byte)0x00
|
||||
};
|
||||
|
||||
private static void waitAndDiscardResponse(InputStream in) throws IOException
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
|
||||
public static void performHandshake(String host) throws UnknownHostException, IOException
|
||||
{
|
||||
Socket s = new Socket(host, PORT);
|
||||
OutputStream out = s.getOutputStream();
|
||||
InputStream in = s.getInputStream();
|
||||
|
||||
// First packet
|
||||
out.write(new byte[]{0x07, 0x00, 0x00, 0x00});
|
||||
out.write(PLATFORM_HELLO);
|
||||
|
||||
System.out.println("HS: Waiting for hello response");
|
||||
|
||||
waitAndDiscardResponse(in);
|
||||
|
||||
// Second packet
|
||||
out.write(PACKET_2);
|
||||
|
||||
System.out.println("HS: Waiting stage 2 response");
|
||||
|
||||
waitAndDiscardResponse(in);
|
||||
|
||||
// Third packet
|
||||
out.write(PACKET_3);
|
||||
|
||||
System.out.println("HS: Waiting for stage 3 response");
|
||||
|
||||
waitAndDiscardResponse(in);
|
||||
|
||||
// Fourth packet
|
||||
out.write(PACKET_4);
|
||||
out.flush();
|
||||
|
||||
// Done
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
78
src/com/limelight/nvstream/NvInputPacket.java
Normal file
78
src/com/limelight/nvstream/NvInputPacket.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package com.limelight.nvstream;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class NvInputPacket {
|
||||
public static final byte[] HEADER =
|
||||
{
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x18,
|
||||
0x0A,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x14
|
||||
};
|
||||
|
||||
public static final byte[] TAIL =
|
||||
{
|
||||
(byte)0x9C,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x55,
|
||||
0x00
|
||||
};
|
||||
|
||||
public static final short A_FLAG = 0x1000;
|
||||
public static final short B_FLAG = 0x2000;
|
||||
public static final short X_FLAG = 0x4000;
|
||||
public static final short Y_FLAG = (short)0x8000;
|
||||
public static final short UP_FLAG = 0x0001;
|
||||
public static final short DOWN_FLAG = 0x0002;
|
||||
public static final short LEFT_FLAG = 0x0004;
|
||||
public static final short RIGHT_FLAG = 0x0008;
|
||||
public static final short RB_FLAG = 0x0100;
|
||||
public static final short LB_FLAG = 0x0200;
|
||||
public static final short LS_CLK_FLAG = 0x0004;
|
||||
public static final short RS_CLK_FLAG = 0x0008;
|
||||
public static final short PLAY_FLAG = 0x0001;
|
||||
public static final short BACK_FLAG = 0x0002;
|
||||
|
||||
public static final short PACKET_LENGTH = 28;
|
||||
|
||||
private short buttonFlags;
|
||||
private byte leftTrigger;
|
||||
private byte rightTrigger;
|
||||
private short leftStick;
|
||||
private short rightStick;
|
||||
|
||||
public NvInputPacket(short buttonFlags, byte leftTrigger, byte rightTrigger,
|
||||
short leftStick, short rightStick)
|
||||
{
|
||||
this.buttonFlags = buttonFlags;
|
||||
this.leftTrigger = leftTrigger;
|
||||
this.rightTrigger = rightTrigger;
|
||||
this.leftStick = leftStick;
|
||||
this.rightStick = rightStick;
|
||||
}
|
||||
|
||||
public byte[] toWire()
|
||||
{
|
||||
ByteBuffer bb = ByteBuffer.allocate(PACKET_LENGTH).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
bb.put(HEADER);
|
||||
bb.putShort(buttonFlags);
|
||||
bb.put(leftTrigger);
|
||||
bb.put(rightTrigger);
|
||||
bb.putShort(leftStick);
|
||||
bb.putShort(rightStick);
|
||||
bb.put(TAIL);
|
||||
|
||||
return bb.array();
|
||||
}
|
||||
}
|
||||
71
src/com/limelight/nvstream/NvVideoStream.java
Normal file
71
src/com/limelight/nvstream/NvVideoStream.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package com.limelight.nvstream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
public class NvVideoStream {
|
||||
public static final int PORT = 47998;
|
||||
public static final int FIRST_FRAME_PORT = 47996;
|
||||
|
||||
private InputStream getFirstFrame(String host) throws UnknownHostException, IOException
|
||||
{
|
||||
Socket s = new Socket(host, FIRST_FRAME_PORT);
|
||||
return s.getInputStream();
|
||||
}
|
||||
|
||||
public void start(final String host)
|
||||
{
|
||||
new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
System.out.println("VID: Waiting for first frame");
|
||||
InputStream firstFrameStream = getFirstFrame(host);
|
||||
firstFrameStream.read();
|
||||
System.out.println("VID: First frame: "+firstFrameStream.available()+1);
|
||||
firstFrameStream.close();
|
||||
System.out.println("VID: Got first frame");
|
||||
} catch (UnknownHostException e2) {
|
||||
// TODO Auto-generated catch block
|
||||
e2.printStackTrace();
|
||||
return;
|
||||
} catch (IOException e2) {
|
||||
// TODO Auto-generated catch block
|
||||
e2.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
DatagramSocket ds;
|
||||
try {
|
||||
ds = new DatagramSocket(PORT);
|
||||
} catch (SocketException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
DatagramPacket dp = new DatagramPacket(new byte[1500], 1500);
|
||||
|
||||
try {
|
||||
ds.receive(dp);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
|
||||
System.out.println("Got UDP 47998: "+dp.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user