diff --git a/.classpath b/.classpath index 7bc01d9a..fbf44eae 100644 --- a/.classpath +++ b/.classpath @@ -5,5 +5,6 @@ + diff --git a/libs/limelight-common.jar b/libs/limelight-common.jar new file mode 100644 index 00000000..a6546181 Binary files /dev/null and b/libs/limelight-common.jar differ diff --git a/src/com/limelight/Connection.java b/src/com/limelight/Connection.java index b8cf3416..d7f57734 100644 --- a/src/com/limelight/Connection.java +++ b/src/com/limelight/Connection.java @@ -8,12 +8,11 @@ import java.net.UnknownHostException; import org.xmlpull.v1.XmlPullParserException; +import com.limelight.binding.PlatformBinding; import com.limelight.nvstream.NvConnection; -import com.limelight.nvstream.NvHTTP; -import com.limelight.nvstream.NvmDNS; +import com.limelight.nvstream.http.NvHTTP; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; @@ -50,13 +49,6 @@ public class Connection extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Log.v("NvmDNS", "onCreate"); - - - NvmDNS dns = new NvmDNS(); - dns.execute(); - - setContentView(R.layout.activity_connection); this.statusButton = (Button) findViewById(R.id.statusButton); @@ -109,7 +101,8 @@ public class Connection extends Activity { NvHTTP httpConn; String message; try { - httpConn = new NvHTTP(InetAddress.getByName(hostText.getText().toString()), macAddress); + httpConn = new NvHTTP(InetAddress.getByName(hostText.getText().toString()), + macAddress, PlatformBinding.getDeviceName()); try { if (httpConn.getPairState()) { message = "Already paired"; diff --git a/src/com/limelight/Game.java b/src/com/limelight/Game.java index e0ebf889..66da1f29 100644 --- a/src/com/limelight/Game.java +++ b/src/com/limelight/Game.java @@ -1,9 +1,10 @@ package com.limelight; +import com.limelight.binding.PlatformBinding; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnectionListener; -import com.limelight.nvstream.av.video.DecoderRenderer; -import com.limelight.nvstream.input.NvControllerPacket; +import com.limelight.nvstream.av.video.VideoDecoderRenderer; +import com.limelight.nvstream.input.ControllerPacket; import com.limelight.utils.Dialog; import com.limelight.utils.SpinnerDialog; @@ -11,6 +12,7 @@ import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.graphics.PixelFormat; +import android.net.ConnectivityManager; import android.os.Bundle; import android.view.InputDevice; import android.view.KeyEvent; @@ -22,6 +24,7 @@ import android.view.View.OnGenericMotionListener; import android.view.View.OnTouchListener; import android.view.Window; import android.view.WindowManager; +import android.widget.Toast; public class Game extends Activity implements OnGenericMotionListener, OnTouchListener, NvConnectionListener { @@ -77,15 +80,27 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi SharedPreferences prefs = getSharedPreferences(PREFS_FILE_NAME, Context.MODE_MULTI_PROCESS); int drFlags = 0; if (prefs.getBoolean(QUALITY_PREF_STRING, false)) { - drFlags |= DecoderRenderer.FLAG_PREFER_QUALITY; + drFlags |= VideoDecoderRenderer.FLAG_PREFER_QUALITY; } + + // Warn the user if they're on a metered connection + checkDataConnection(); // Start the connection - conn = new NvConnection(Game.this.getIntent().getStringExtra("host"), Game.this, sv.getHolder(), drFlags); - conn.start(); + conn = new NvConnection(Game.this.getIntent().getStringExtra("host"), Game.this); + conn.start(PlatformBinding.getDeviceName(), sv.getHolder(), drFlags, + PlatformBinding.getAudioRenderer(), PlatformBinding.chooseDecoderRenderer()); + } + + private void checkDataConnection() + { + ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + if (mgr.isActiveNetworkMetered()) { + displayMessage("Warning: Your active network connection is metered!"); + } } - public void hideSystemUi() { + private void hideSystemUi() { runOnUiThread(new Runnable() { @Override public void run() { @@ -128,58 +143,58 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi switch (keyCode) { case KeyEvent.KEYCODE_BUTTON_START: case KeyEvent.KEYCODE_MENU: - inputMap |= NvControllerPacket.PLAY_FLAG; + inputMap |= ControllerPacket.PLAY_FLAG; break; case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_BUTTON_SELECT: - inputMap |= NvControllerPacket.BACK_FLAG; + inputMap |= ControllerPacket.BACK_FLAG; break; case KeyEvent.KEYCODE_DPAD_LEFT: - inputMap |= NvControllerPacket.LEFT_FLAG; + inputMap |= ControllerPacket.LEFT_FLAG; break; case KeyEvent.KEYCODE_DPAD_RIGHT: - inputMap |= NvControllerPacket.RIGHT_FLAG; + inputMap |= ControllerPacket.RIGHT_FLAG; break; case KeyEvent.KEYCODE_DPAD_UP: - inputMap |= NvControllerPacket.UP_FLAG; + inputMap |= ControllerPacket.UP_FLAG; break; case KeyEvent.KEYCODE_DPAD_DOWN: - inputMap |= NvControllerPacket.DOWN_FLAG; + inputMap |= ControllerPacket.DOWN_FLAG; break; case KeyEvent.KEYCODE_BUTTON_B: - inputMap |= NvControllerPacket.B_FLAG; + inputMap |= ControllerPacket.B_FLAG; break; case KeyEvent.KEYCODE_BUTTON_A: - inputMap |= NvControllerPacket.A_FLAG; + inputMap |= ControllerPacket.A_FLAG; break; case KeyEvent.KEYCODE_BUTTON_X: - inputMap |= NvControllerPacket.X_FLAG; + inputMap |= ControllerPacket.X_FLAG; break; case KeyEvent.KEYCODE_BUTTON_Y: - inputMap |= NvControllerPacket.Y_FLAG; + inputMap |= ControllerPacket.Y_FLAG; break; case KeyEvent.KEYCODE_BUTTON_L1: - inputMap |= NvControllerPacket.LB_FLAG; + inputMap |= ControllerPacket.LB_FLAG; break; case KeyEvent.KEYCODE_BUTTON_R1: - inputMap |= NvControllerPacket.RB_FLAG; + inputMap |= ControllerPacket.RB_FLAG; break; case KeyEvent.KEYCODE_BUTTON_THUMBL: - inputMap |= NvControllerPacket.LS_CLK_FLAG; + inputMap |= ControllerPacket.LS_CLK_FLAG; break; case KeyEvent.KEYCODE_BUTTON_THUMBR: - inputMap |= NvControllerPacket.RS_CLK_FLAG; + inputMap |= ControllerPacket.RS_CLK_FLAG; break; default: return super.onKeyDown(keyCode, event); } // We detect back+start as the special button combo - if ((inputMap & NvControllerPacket.BACK_FLAG) != 0 && - (inputMap & NvControllerPacket.PLAY_FLAG) != 0) + if ((inputMap & ControllerPacket.BACK_FLAG) != 0 && + (inputMap & ControllerPacket.PLAY_FLAG) != 0) { - inputMap &= ~(NvControllerPacket.BACK_FLAG | NvControllerPacket.PLAY_FLAG); - inputMap |= NvControllerPacket.SPECIAL_BUTTON_FLAG; + inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG); + inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG; } sendControllerInputPacket(); @@ -191,57 +206,57 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi switch (keyCode) { case KeyEvent.KEYCODE_BUTTON_START: case KeyEvent.KEYCODE_MENU: - inputMap &= ~NvControllerPacket.PLAY_FLAG; + inputMap &= ~ControllerPacket.PLAY_FLAG; break; case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_BUTTON_SELECT: - inputMap &= ~NvControllerPacket.BACK_FLAG; + inputMap &= ~ControllerPacket.BACK_FLAG; break; case KeyEvent.KEYCODE_DPAD_LEFT: - inputMap &= ~NvControllerPacket.LEFT_FLAG; + inputMap &= ~ControllerPacket.LEFT_FLAG; break; case KeyEvent.KEYCODE_DPAD_RIGHT: - inputMap &= ~NvControllerPacket.RIGHT_FLAG; + inputMap &= ~ControllerPacket.RIGHT_FLAG; break; case KeyEvent.KEYCODE_DPAD_UP: - inputMap &= ~NvControllerPacket.UP_FLAG; + inputMap &= ~ControllerPacket.UP_FLAG; break; case KeyEvent.KEYCODE_DPAD_DOWN: - inputMap &= ~NvControllerPacket.DOWN_FLAG; + inputMap &= ~ControllerPacket.DOWN_FLAG; break; case KeyEvent.KEYCODE_BUTTON_B: - inputMap &= ~NvControllerPacket.B_FLAG; + inputMap &= ~ControllerPacket.B_FLAG; break; case KeyEvent.KEYCODE_BUTTON_A: - inputMap &= ~NvControllerPacket.A_FLAG; + inputMap &= ~ControllerPacket.A_FLAG; break; case KeyEvent.KEYCODE_BUTTON_X: - inputMap &= ~NvControllerPacket.X_FLAG; + inputMap &= ~ControllerPacket.X_FLAG; break; case KeyEvent.KEYCODE_BUTTON_Y: - inputMap &= ~NvControllerPacket.Y_FLAG; + inputMap &= ~ControllerPacket.Y_FLAG; break; case KeyEvent.KEYCODE_BUTTON_L1: - inputMap &= ~NvControllerPacket.LB_FLAG; + inputMap &= ~ControllerPacket.LB_FLAG; break; case KeyEvent.KEYCODE_BUTTON_R1: - inputMap &= ~NvControllerPacket.RB_FLAG; + inputMap &= ~ControllerPacket.RB_FLAG; break; case KeyEvent.KEYCODE_BUTTON_THUMBL: - inputMap &= ~NvControllerPacket.LS_CLK_FLAG; + inputMap &= ~ControllerPacket.LS_CLK_FLAG; break; case KeyEvent.KEYCODE_BUTTON_THUMBR: - inputMap &= ~NvControllerPacket.RS_CLK_FLAG; + inputMap &= ~ControllerPacket.RS_CLK_FLAG; break; default: return super.onKeyUp(keyCode, event); } // If one of the two is up, the special button comes up too - if ((inputMap & NvControllerPacket.BACK_FLAG) == 0 || - (inputMap & NvControllerPacket.PLAY_FLAG) == 0) + if ((inputMap & ControllerPacket.BACK_FLAG) == 0 || + (inputMap & ControllerPacket.PLAY_FLAG) == 0) { - inputMap &= ~NvControllerPacket.SPECIAL_BUTTON_FLAG; + inputMap &= ~ControllerPacket.SPECIAL_BUTTON_FLAG; } sendControllerInputPacket(); @@ -398,19 +413,19 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi hatX = event.getAxisValue(MotionEvent.AXIS_HAT_X); hatY = event.getAxisValue(MotionEvent.AXIS_HAT_Y); - inputMap &= ~(NvControllerPacket.LEFT_FLAG | NvControllerPacket.RIGHT_FLAG); - inputMap &= ~(NvControllerPacket.UP_FLAG | NvControllerPacket.DOWN_FLAG); + inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG); + inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG); if (hatX < -0.5) { - inputMap |= NvControllerPacket.LEFT_FLAG; + inputMap |= ControllerPacket.LEFT_FLAG; } if (hatX > 0.5) { - inputMap |= NvControllerPacket.RIGHT_FLAG; + inputMap |= ControllerPacket.RIGHT_FLAG; } if (hatY < -0.5) { - inputMap |= NvControllerPacket.UP_FLAG; + inputMap |= ControllerPacket.UP_FLAG; } if (hatY > 0.5) { - inputMap |= NvControllerPacket.DOWN_FLAG; + inputMap |= ControllerPacket.DOWN_FLAG; } } @@ -506,5 +521,17 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi public void connectionStarted() { spinner.dismiss(); spinner = null; + + hideSystemUi(); + } + + @Override + public void displayMessage(final String message) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(Game.this, message, Toast.LENGTH_LONG).show(); + } + }); } } diff --git a/src/com/limelight/binding/PlatformBinding.java b/src/com/limelight/binding/PlatformBinding.java new file mode 100644 index 00000000..3544d429 --- /dev/null +++ b/src/com/limelight/binding/PlatformBinding.java @@ -0,0 +1,36 @@ +package com.limelight.binding; + +import android.os.Build; + +import com.limelight.binding.audio.AndroidAudioRenderer; +import com.limelight.binding.video.AndroidCpuDecoderRenderer; +import com.limelight.binding.video.MediaCodecDecoderRenderer; +import com.limelight.nvstream.av.audio.AudioRenderer; +import com.limelight.nvstream.av.video.VideoDecoderRenderer; + +public class PlatformBinding { + public static VideoDecoderRenderer chooseDecoderRenderer() { + if (Build.HARDWARE.equals("goldfish")) { + // Emulator - don't render video (it's slow!) + return null; + } + /*else if (MediaCodecDecoderRenderer.findSafeDecoder() != null) { + // Hardware decoding + return new MediaCodecDecoderRenderer(); + }*/ + else { + // Software decoding + return new AndroidCpuDecoderRenderer(); + } + } + + public static String getDeviceName() { + String deviceName = android.os.Build.MODEL; + deviceName = deviceName.replace(" ", ""); + return deviceName; + } + + public static AudioRenderer getAudioRenderer() { + return new AndroidAudioRenderer(); + } +} diff --git a/src/com/limelight/binding/audio/AndroidAudioRenderer.java b/src/com/limelight/binding/audio/AndroidAudioRenderer.java new file mode 100644 index 00000000..d02e3cd6 --- /dev/null +++ b/src/com/limelight/binding/audio/AndroidAudioRenderer.java @@ -0,0 +1,50 @@ +package com.limelight.binding.audio; + +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; + +import com.limelight.nvstream.av.audio.AudioRenderer; + +public class AndroidAudioRenderer implements AudioRenderer { + + private AudioTrack track; + + @Override + public void streamInitialized(int channelCount, int sampleRate) { + int channelConfig; + + switch (channelCount) + { + case 1: + channelConfig = AudioFormat.CHANNEL_OUT_MONO; + break; + case 2: + channelConfig = AudioFormat.CHANNEL_OUT_STEREO; + break; + default: + throw new IllegalArgumentException("Decoder returned unhandled channel count"); + } + + track = new AudioTrack(AudioManager.STREAM_MUSIC, + sampleRate, + channelConfig, + AudioFormat.ENCODING_PCM_16BIT, + 1024, // 1KB buffer + AudioTrack.MODE_STREAM); + + track.play(); + } + + @Override + public void playDecodedAudio(short[] audioData, int offset, int length) { + track.write(audioData, offset, length); + } + + @Override + public void streamClosing() { + if (track != null) { + track.release(); + } + } +} diff --git a/src/com/limelight/nvstream/av/video/cpu/CpuDecoderRenderer.java b/src/com/limelight/binding/video/AndroidCpuDecoderRenderer.java similarity index 80% rename from src/com/limelight/nvstream/av/video/cpu/CpuDecoderRenderer.java rename to src/com/limelight/binding/video/AndroidCpuDecoderRenderer.java index a749c019..4f4adb5f 100644 --- a/src/com/limelight/nvstream/av/video/cpu/CpuDecoderRenderer.java +++ b/src/com/limelight/binding/video/AndroidCpuDecoderRenderer.java @@ -1,4 +1,4 @@ -package com.limelight.nvstream.av.video.cpu; +package com.limelight.binding.video; import java.io.BufferedReader; import java.io.File; @@ -6,14 +6,14 @@ import java.io.FileReader; import java.io.IOException; import java.nio.ByteBuffer; -import android.content.Context; import android.view.SurfaceHolder; -import com.limelight.nvstream.av.AvByteBufferDescriptor; -import com.limelight.nvstream.av.AvDecodeUnit; -import com.limelight.nvstream.av.video.DecoderRenderer; +import com.limelight.nvstream.av.ByteBufferDescriptor; +import com.limelight.nvstream.av.DecodeUnit; +import com.limelight.nvstream.av.video.VideoDecoderRenderer; +import com.limelight.nvstream.av.video.cpu.AvcDecoder; -public class CpuDecoderRenderer implements DecoderRenderer { +public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer { private Thread rendererThread; private int targetFps; @@ -21,9 +21,6 @@ public class CpuDecoderRenderer implements DecoderRenderer { private static final int DECODER_BUFFER_SIZE = 92*1024; private ByteBuffer decoderBuffer; - private RsRenderer rsRenderer; - private byte[] frameBuffer; - // Only sleep if the difference is above this value private static final int WAIT_CEILING_MS = 8; @@ -81,7 +78,7 @@ public class CpuDecoderRenderer implements DecoderRenderer { } @Override - public void setup(Context context, int width, int height, SurfaceHolder renderTarget, int drFlags) { + public void setup(int width, int height, Object renderTarget, int drFlags) { this.targetFps = 30; int perfLevel = findOptimalPerformanceLevel(); @@ -115,14 +112,8 @@ public class CpuDecoderRenderer implements DecoderRenderer { break; } - // Create and initialize the RenderScript intrinsic we'll be using - rsRenderer = new RsRenderer(context, width, height, renderTarget.getSurface()); - - // Allocate the frame buffer that the RGBA frame will be copied into - frameBuffer = new byte[width*height*4]; - // If the user wants quality, we'll remove the low IQ flags - if ((drFlags & DecoderRenderer.FLAG_PREFER_QUALITY) != 0) { + if ((drFlags & VideoDecoderRenderer.FLAG_PREFER_QUALITY) != 0) { // Make sure the loop filter is enabled avcFlags &= ~AvcDecoder.DISABLE_LOOP_FILTER; @@ -137,6 +128,8 @@ public class CpuDecoderRenderer implements DecoderRenderer { throw new IllegalStateException("AVC decoder initialization failure: "+err); } + AvcDecoder.setRenderTarget(((SurfaceHolder)renderTarget).getSurface()); + decoderBuffer = ByteBuffer.allocate(DECODER_BUFFER_SIZE + AvcDecoder.getInputPaddingSize()); System.out.println("Using software decoding (performance level: "+perfLevel+")"); @@ -162,9 +155,7 @@ public class CpuDecoderRenderer implements DecoderRenderer { } nextFrameTime = computePresentationTimeMs(targetFps); - if (AvcDecoder.getRgbFrame(frameBuffer, frameBuffer.length)) { - rsRenderer.render(frameBuffer); - } + AvcDecoder.redraw(); } } }; @@ -187,22 +178,18 @@ public class CpuDecoderRenderer implements DecoderRenderer { @Override public void release() { - if (rsRenderer != null) { - rsRenderer.release(); - } - AvcDecoder.destroy(); } @Override - public boolean submitDecodeUnit(AvDecodeUnit decodeUnit) { + public boolean submitDecodeUnit(DecodeUnit decodeUnit) { byte[] data; // Use the reserved decoder buffer if this decode unit will fit if (decodeUnit.getDataLength() <= DECODER_BUFFER_SIZE) { decoderBuffer.clear(); - for (AvByteBufferDescriptor bbd : decodeUnit.getBufferList()) { + for (ByteBufferDescriptor bbd : decodeUnit.getBufferList()) { decoderBuffer.put(bbd.data, bbd.offset, bbd.length); } @@ -212,7 +199,7 @@ public class CpuDecoderRenderer implements DecoderRenderer { data = new byte[decodeUnit.getDataLength()+AvcDecoder.getInputPaddingSize()]; int offset = 0; - for (AvByteBufferDescriptor bbd : decodeUnit.getBufferList()) { + for (ByteBufferDescriptor bbd : decodeUnit.getBufferList()) { System.arraycopy(bbd.data, bbd.offset, data, offset, bbd.length); offset += bbd.length; } diff --git a/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java b/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java similarity index 85% rename from src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java rename to src/com/limelight/binding/video/MediaCodecDecoderRenderer.java index c4226085..9aa450fb 100644 --- a/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java +++ b/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -1,24 +1,21 @@ -package com.limelight.nvstream.av.video; +package com.limelight.binding.video; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; -import com.limelight.nvstream.av.AvByteBufferDescriptor; -import com.limelight.nvstream.av.AvDecodeUnit; +import com.limelight.nvstream.av.ByteBufferDescriptor; +import com.limelight.nvstream.av.DecodeUnit; +import com.limelight.nvstream.av.video.VideoDecoderRenderer; -import android.annotation.TargetApi; -import android.content.Context; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.media.MediaFormat; import android.media.MediaCodec.BufferInfo; -import android.os.Build; import android.view.SurfaceHolder; -@TargetApi(Build.VERSION_CODES.JELLY_BEAN) -public class MediaCodecDecoderRenderer implements DecoderRenderer { +public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { private ByteBuffer[] videoDecoderInputBuffers; private MediaCodec videoDecoder; @@ -74,11 +71,11 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer { } @Override - public void setup(Context context, int width, int height, SurfaceHolder renderTarget, int drFlags) { + public void setup(int width, int height, Object renderTarget, int drFlags) { videoDecoder = MediaCodec.createByCodecName(findSafeDecoder().getName()); MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height); - videoDecoder.configure(videoFormat, renderTarget.getSurface(), null, 0); + videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0); videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); @@ -157,8 +154,8 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer { } @Override - public boolean submitDecodeUnit(AvDecodeUnit decodeUnit) { - if (decodeUnit.getType() != AvDecodeUnit.TYPE_H264) { + public boolean submitDecodeUnit(DecodeUnit decodeUnit) { + if (decodeUnit.getType() != DecodeUnit.TYPE_H264) { System.err.println("Unknown decode unit type"); return false; } @@ -172,7 +169,7 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer { buf.clear(); // Copy data from our buffer list into the input buffer - for (AvByteBufferDescriptor desc : decodeUnit.getBufferList()) + for (ByteBufferDescriptor desc : decodeUnit.getBufferList()) { buf.put(desc.data, desc.offset, desc.length); } diff --git a/src/com/limelight/nvstream/av/video/cpu/RsRenderer.java b/src/com/limelight/binding/video/RsRenderer.java similarity index 95% rename from src/com/limelight/nvstream/av/video/cpu/RsRenderer.java rename to src/com/limelight/binding/video/RsRenderer.java index d461e13d..7491bfed 100644 --- a/src/com/limelight/nvstream/av/video/cpu/RsRenderer.java +++ b/src/com/limelight/binding/video/RsRenderer.java @@ -1,4 +1,4 @@ -package com.limelight.nvstream.av.video.cpu; +package com.limelight.binding.video; import android.content.Context; import android.renderscript.Allocation; diff --git a/src/com/limelight/nvstream/NvApp.java b/src/com/limelight/nvstream/NvApp.java deleted file mode 100644 index e30c7820..00000000 --- a/src/com/limelight/nvstream/NvApp.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.limelight.nvstream; - -public class NvApp { - private String appName; - private int appId; - private boolean isRunning; - - public void setAppName(String appName) { - this.appName = appName; - } - - public void setAppId(String appId) { - this.appId = Integer.parseInt(appId); - } - - public void setIsRunning(String isRunning) { - this.isRunning = isRunning.equals("1"); - } - - public String getAppName() { - return this.appName; - } - - public int getAppId() { - return this.appId; - } - - public boolean getIsRunning() { - return this.isRunning; - } -} diff --git a/src/com/limelight/nvstream/NvAudioStream.java b/src/com/limelight/nvstream/NvAudioStream.java deleted file mode 100644 index 9c722924..00000000 --- a/src/com/limelight/nvstream/NvAudioStream.java +++ /dev/null @@ -1,250 +0,0 @@ -package com.limelight.nvstream; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.util.LinkedList; -import java.util.concurrent.LinkedBlockingQueue; - -import com.limelight.nvstream.av.AvByteBufferDescriptor; -import com.limelight.nvstream.av.AvRtpPacket; -import com.limelight.nvstream.av.AvShortBufferDescriptor; -import com.limelight.nvstream.av.audio.AvAudioDepacketizer; -import com.limelight.nvstream.av.audio.OpusDecoder; - -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioTrack; - -public class NvAudioStream { - public static final int RTP_PORT = 48000; - public static final int RTCP_PORT = 47999; - - private LinkedBlockingQueue packets = new LinkedBlockingQueue(100); - - private AudioTrack track; - - private DatagramSocket rtp; - - private AvAudioDepacketizer depacketizer = new AvAudioDepacketizer(); - - private LinkedList threads = new LinkedList(); - - 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) { - return; - } - - aborting = true; - - for (Thread t : threads) { - t.interrupt(); - } - - // Close the socket to interrupt the receive thread - if (rtp != null) { - rtp.close(); - } - - // Wait for threads to terminate - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException e) { } - } - - if (track != null) { - track.release(); - } - - threads.clear(); - } - - public void startAudioStream() throws SocketException - { - setupRtpSession(); - - setupAudio(); - - startReceiveThread(); - - startDepacketizerThread(); - - startDecoderThread(); - - startUdpPingThread(); - } - - private void setupRtpSession() throws SocketException - { - rtp = new DatagramSocket(RTP_PORT); - } - - private void setupAudio() - { - int channelConfig; - int err; - - err = OpusDecoder.init(); - if (err != 0) { - throw new IllegalStateException("Opus decoder failed to initialize"); - } - - switch (OpusDecoder.getChannelCount()) - { - case 1: - channelConfig = AudioFormat.CHANNEL_OUT_MONO; - break; - case 2: - channelConfig = AudioFormat.CHANNEL_OUT_STEREO; - break; - default: - throw new IllegalStateException("Opus decoder returned unhandled channel count"); - } - - track = new AudioTrack(AudioManager.STREAM_MUSIC, - OpusDecoder.getSampleRate(), - channelConfig, - AudioFormat.ENCODING_PCM_16BIT, - 1024, // 1KB buffer - AudioTrack.MODE_STREAM); - - track.play(); - } - - private void startDepacketizerThread() - { - // This thread lessens the work on the receive thread - // so it can spend more time waiting for data - Thread t = new Thread() { - @Override - public void run() { - while (!isInterrupted()) - { - AvRtpPacket packet; - - try { - packet = packets.take(); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - - depacketizer.decodeInputData(packet); - } - } - }; - threads.add(t); - t.setName("Audio - Depacketizer"); - t.start(); - } - - private void startDecoderThread() - { - // Decoder thread - Thread t = new Thread() { - @Override - public void run() { - while (!isInterrupted()) - { - AvShortBufferDescriptor samples; - - try { - samples = depacketizer.getNextDecodedData(); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - - track.write(samples.data, samples.offset, samples.length); - } - } - }; - threads.add(t); - t.setName("Audio - Player"); - t.start(); - } - - private void startReceiveThread() - { - // Receive thread - Thread t = new Thread() { - @Override - public void run() { - AvByteBufferDescriptor desc = new AvByteBufferDescriptor(new byte[1500], 0, 1500); - DatagramPacket packet = new DatagramPacket(desc.data, desc.length); - - while (!isInterrupted()) - { - try { - rtp.receive(packet); - } catch (IOException e) { - listener.connectionTerminated(e); - return; - } - - // Give the packet to the depacketizer thread - desc.length = packet.getLength(); - if (packets.offer(new AvRtpPacket(desc))) { - desc.reinitialize(new byte[1500], 0, 1500); - packet.setData(desc.data, desc.offset, desc.length); - } - } - } - }; - threads.add(t); - t.setName("Audio - Receive"); - t.start(); - } - - private void startUdpPingThread() - { - // Ping thread - Thread t = new Thread() { - @Override - public void run() { - // PING in ASCII - final byte[] pingPacketData = new byte[] {0x50, 0x49, 0x4E, 0x47}; - DatagramPacket pingPacket = new DatagramPacket(pingPacketData, pingPacketData.length); - pingPacket.setSocketAddress(new InetSocketAddress(host, RTP_PORT)); - - // Send PING every 100 ms - while (!isInterrupted()) - { - try { - rtp.send(pingPacket); - } catch (IOException e) { - listener.connectionTerminated(e); - return; - } - - try { - Thread.sleep(100); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - } - } - }; - threads.add(t); - t.setPriority(Thread.MIN_PRIORITY); - t.setName("Audio - Ping"); - t.start(); - } -} diff --git a/src/com/limelight/nvstream/NvComputer.java b/src/com/limelight/nvstream/NvComputer.java deleted file mode 100644 index 3fb6d57f..00000000 --- a/src/com/limelight/nvstream/NvComputer.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.limelight.nvstream; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.SocketException; -import java.util.Locale; -import java.util.UUID; - -import org.xmlpull.v1.XmlPullParserException; - -import android.util.Log; - -public class NvComputer { - private String hostname; - private InetAddress ipAddress; - private String ipAddressString; - private int state; - private int numOfApps; - private String gpuType; - private String mac; - private UUID uniqueID; - - private NvHTTP nvHTTP; - - - private int sessionID; - private boolean pairState; - private boolean isBusy; - - public NvComputer(String hostname, InetAddress ipAddress, int state, int numOfApps, String gpuType, String mac, UUID uniqueID) { - this.hostname = hostname; - this.ipAddress = ipAddress; - this.ipAddressString = this.ipAddress.getHostAddress(); - this.state = state; - this.numOfApps = numOfApps; - this.gpuType = gpuType; - this.mac = mac; - this.uniqueID = uniqueID; - - try { - 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.ipAddress, "00:00:00:00:00:00"); - } - - this.updatePairState(); - } - - public String getHostname() { - return this.hostname; - } - - public InetAddress getIpAddress() { - return this.ipAddress; - } - - public String getIpAddressString() { - return this.ipAddressString; - } - - public int getState() { - return this.state; - } - - public int getNumOfApps() { - return this.numOfApps; - } - - public String getGpuType() { - return this.gpuType; - } - - public String getMac() { - return this.mac; - } - - public UUID getUniqueID() { - return this.uniqueID; - } - - public void updateAfterPairQuery(int sessionID, boolean paired, boolean isBusy) { - this.sessionID = sessionID; - this.pairState = paired; - this.isBusy = isBusy; - } - - public int getSessionID() { - return this.sessionID; - } - - public void updatePairState() { - try { - this.pairState = this.nvHTTP.getPairState(); - } catch (IOException e) { - Log.e("NvComputer UpdatePaired", "Unable to get Pair State " + e.getMessage()); - this.pairState = false; - } catch (XmlPullParserException e) { - Log.e("NvComputer UpdatePaired", "Unable to get Pair State " + e.getMessage()); - this.pairState = false; - } - - /*if (this.pairState == true) { - try { - this.sessionID = this.nvHTTP.getSessionId(); - } catch (IOException e) { - Log.e("NvComputer UpdatePaired", "Unable to get Session ID " + e.getMessage()); - this.sessionID = 0; - } catch (XmlPullParserException e) { - Log.e("NvComputer UpdatePaired", "Unable to get Session ID " + e.getMessage()); - this.sessionID = 0; - } - - }*/ - } - - public boolean getPairState() { - return this.pairState; - } - - public boolean getIsBusy() { - return this.isBusy; - } - - public int hashCode() { - if (this.ipAddress == null) { - return -1; - } else { - return this.ipAddressString.hashCode(); - } - } - - public String toString() { - StringBuilder returnStringBuilder = new StringBuilder(); - returnStringBuilder.append("NvComputer 0x"); - returnStringBuilder.append(Integer.toHexString(this.hashCode()).toUpperCase(Locale.getDefault())); - returnStringBuilder.append("\n|- Hostname: "); - returnStringBuilder.append(this.hostname); - returnStringBuilder.append("\n|- IP Address: "); - returnStringBuilder.append(this.ipAddressString); - returnStringBuilder.append("\n|- Computer State: "); - returnStringBuilder.append(this.state); - returnStringBuilder.append("\n|- Number of Apps: "); - returnStringBuilder.append(this.numOfApps); - returnStringBuilder.append("\n|- GPU: "); - returnStringBuilder.append(this.gpuType); - returnStringBuilder.append("\n|- MAC: "); - returnStringBuilder.append(this.mac); - returnStringBuilder.append("\n|- UniqueID: "); - returnStringBuilder.append(this.uniqueID); - returnStringBuilder.append("\n\\- Pair State: "); - returnStringBuilder.append(this.pairState); - returnStringBuilder.append("\n"); - return returnStringBuilder.toString(); - } - - public boolean equals(Object obj) { - if (obj instanceof UUID) { - return this.uniqueID.equals(obj); - } else { - return false; - } - } -} \ No newline at end of file diff --git a/src/com/limelight/nvstream/NvConnection.java b/src/com/limelight/nvstream/NvConnection.java deleted file mode 100644 index 5f044722..00000000 --- a/src/com/limelight/nvstream/NvConnection.java +++ /dev/null @@ -1,338 +0,0 @@ -package com.limelight.nvstream; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Enumeration; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.xmlpull.v1.XmlPullParserException; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.view.SurfaceHolder; -import android.widget.Toast; - -import com.limelight.Game; -import com.limelight.nvstream.input.NvController; - -public class NvConnection { - private String host; - private Game activity; - private NvConnectionListener listener; - private int drFlags; - - private InetAddress hostAddr; - private NvControl controlStream; - private NvController inputStream; - private SurfaceHolder video; - private NvVideoStream videoStream; - private NvAudioStream audioStream; - - private ThreadPoolExecutor threadPool; - - public NvConnection(String host, Game activity, SurfaceHolder video, int drFlags) - { - this.host = host; - this.listener = activity; - this.activity = activity; - this.video = video; - this.drFlags = drFlags; - this.threadPool = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue()); - } - - public static String getMacAddressString() throws SocketException { - Enumeration ifaceList; - NetworkInterface selectedIface = null; - - // First look for a WLAN interface (since those generally aren't removable) - ifaceList = NetworkInterface.getNetworkInterfaces(); - while (selectedIface == null && ifaceList.hasMoreElements()) { - NetworkInterface iface = ifaceList.nextElement(); - - if (iface.getName().startsWith("wlan") && - iface.getHardwareAddress() != null) { - selectedIface = iface; - } - } - - // If we didn't find that, look for an Ethernet interface - ifaceList = NetworkInterface.getNetworkInterfaces(); - while (selectedIface == null && ifaceList.hasMoreElements()) { - NetworkInterface iface = ifaceList.nextElement(); - - if (iface.getName().startsWith("eth") && - iface.getHardwareAddress() != null) { - selectedIface = iface; - } - } - - // Now just find something with a MAC address - ifaceList = NetworkInterface.getNetworkInterfaces(); - while (selectedIface == null && ifaceList.hasMoreElements()) { - NetworkInterface iface = ifaceList.nextElement(); - - if (iface.getHardwareAddress() != null) { - selectedIface = ifaceList.nextElement(); - break; - } - } - - if (selectedIface == null) { - return null; - } - - byte[] macAddress = selectedIface.getHardwareAddress(); - if (macAddress != null) { - StringBuilder addrStr = new StringBuilder(); - for (int i = 0; i < macAddress.length; i++) { - addrStr.append(String.format("%02x", macAddress[i])); - if (i != macAddress.length - 1) { - addrStr.append(':'); - } - } - return addrStr.toString(); - } - - return null; - } - - public void stop() - { - threadPool.shutdownNow(); - - if (videoStream != null) { - videoStream.abort(); - } - if (audioStream != null) { - audioStream.abort(); - } - - if (controlStream != null) { - controlStream.abort(); - } - - if (inputStream != null) { - inputStream.close(); - 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(activity, video, drFlags); - 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) { - e.printStackTrace(); - success = false; - } - - if (success) { - listener.stageComplete(currentStage); - } - else { - listener.stageFailed(currentStage); - return; - } - } - - listener.connectionStarted(); - } - - public void start() - { - new Thread(new Runnable() { - @Override - public void run() { - checkDataConnection(); - - try { - hostAddr = InetAddress.getByName(host); - } catch (UnknownHostException e) { - displayToast(e.getMessage()); - listener.connectionTerminated(e); - return; - } - - establishConnection(); - - activity.hideSystemUi(); - } - }).start(); - } - - private void checkDataConnection() - { - ConnectivityManager mgr = (ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE); - if (mgr.isActiveNetworkMetered()) { - displayToast("Warning: Your active network connection is metered!"); - } - } - - public void sendMouseMove(final short deltaX, final short deltaY) - { - if (inputStream == null) - return; - - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - inputStream.sendMouseMove(deltaX, deltaY); - } catch (IOException e) { - listener.connectionTerminated(e); - } - } - }); - } - - public void sendMouseButtonDown() - { - if (inputStream == null) - return; - - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - inputStream.sendMouseButtonDown(); - } catch (IOException e) { - listener.connectionTerminated(e); - } - } - }); - } - - public void sendMouseButtonUp() - { - if (inputStream == null) - return; - - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - inputStream.sendMouseButtonUp(); - } catch (IOException e) { - listener.connectionTerminated(e); - } - } - }); - } - - public void sendControllerInput(final short buttonFlags, - final byte leftTrigger, final byte rightTrigger, - final short leftStickX, final short leftStickY, - final short rightStickX, final short rightStickY) - { - if (inputStream == null) - return; - - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - inputStream.sendControllerInput(buttonFlags, leftTrigger, - rightTrigger, leftStickX, leftStickY, - rightStickX, rightStickY); - } catch (IOException e) { - listener.connectionTerminated(e); - } - } - }); - } - - private void displayToast(final String message) - { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); - } - }); - } -} diff --git a/src/com/limelight/nvstream/NvConnectionListener.java b/src/com/limelight/nvstream/NvConnectionListener.java deleted file mode 100644 index c929423b..00000000 --- a/src/com/limelight/nvstream/NvConnectionListener.java +++ /dev/null @@ -1,30 +0,0 @@ -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(Exception e); -} diff --git a/src/com/limelight/nvstream/NvControl.java b/src/com/limelight/nvstream/NvControl.java deleted file mode 100644 index ba76045a..00000000 --- a/src/com/limelight/nvstream/NvControl.java +++ /dev/null @@ -1,505 +0,0 @@ -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.nio.ByteBuffer; -import java.nio.ByteOrder; - -import com.limelight.nvstream.av.ConnectionStatusListener; - -public class NvControl implements ConnectionStatusListener { - - public static final int PORT = 47995; - - public static final int CONTROL_TIMEOUT = 5000; - - public static final short PTYPE_HELLO = 0x1204; - public static final short PPAYLEN_HELLO = 0x0004; - public static final byte[] PPAYLOAD_HELLO = - { - (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_RESYNC = 0x1404; - public static final short PPAYLEN_RESYNC = 16; - - 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, - 167773202, - 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_JITTER = 0x140c; - public static final short PPAYLEN_JITTER = 0x10; - - private int seqNum; - - private NvConnectionListener listener; - private InetAddress host; - - private Socket s; - private InputStream in; - private OutputStream out; - - private Thread heartbeatThread; - private Thread jitterThread; - private Thread resyncThread; - private Object resyncNeeded = new Object(); - private boolean aborting = false; - - public NvControl(InetAddress host, NvConnectionListener listener) - { - this.listener = listener; - this.host = host; - } - - public void initialize() throws IOException - { - s = new Socket(); - s.setSoTimeout(CONTROL_TIMEOUT); - s.setTcpNoDelay(true); - s.connect(new InetSocketAddress(host, PORT), CONTROL_TIMEOUT); - in = s.getInputStream(); - out = s.getOutputStream(); - } - - private void sendPacket(NvCtlPacket packet) throws IOException - { - out.write(packet.toWire()); - out.flush(); - } - - private NvControl.NvCtlResponse sendAndGetReply(NvCtlPacket packet) throws IOException - { - sendPacket(packet); - return new NvCtlResponse(in); - } - - private void sendJitter() throws IOException - { - ByteBuffer bb = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN); - - bb.putInt(0); - bb.putInt(77); - bb.putInt(888); - bb.putInt(seqNum += 2); - - sendPacket(new NvCtlPacket(PTYPE_JITTER, PPAYLEN_JITTER, bb.array())); - } - - public void abort() - { - if (aborting) { - return; - } - - aborting = true; - - if (jitterThread != null) { - jitterThread.interrupt(); - } - - if (heartbeatThread != null) { - heartbeatThread.interrupt(); - } - - try { - s.close(); - } catch (IOException e) {} - } - - public void requestResync() throws IOException - { - System.out.println("CTL: Requesting IDR frame"); - sendResync(); - } - - public void start() throws IOException - { - sendHello(); - sendConfig(); - pingPong(); - send1405AndGetResponse(); - - heartbeatThread = new Thread() { - @Override - public void run() { - while (!isInterrupted()) - { - try { - sendHeartbeat(); - } catch (IOException e) { - listener.connectionTerminated(e); - return; - } - - - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - } - } - }; - heartbeatThread.start(); - - resyncThread = new Thread() { - @Override - public void run() { - while (!isInterrupted()) - { - try { - // Wait for notification of a resync needed - synchronized (resyncNeeded) { - resyncNeeded.wait(); - } - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - - try { - requestResync(); - } catch (IOException e) { - listener.connectionTerminated(e); - return; - } - } - } - }; - resyncThread.start(); - } - - public void startJitterPackets() - { - jitterThread = new Thread() { - @Override - public void run() { - while (!isInterrupted()) - { - try { - sendJitter(); - } catch (IOException e) { - listener.connectionTerminated(e); - return; - } - - try { - Thread.sleep(100); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - } - } - }; - jitterThread.start(); - } - - private NvControl.NvCtlResponse send1405AndGetResponse() throws IOException - { - return sendAndGetReply(new NvCtlPacket(PTYPE_1405, PPAYLEN_1405)); - } - - private void sendHello() throws IOException - { - sendPacket(new NvCtlPacket(PTYPE_HELLO, PPAYLEN_HELLO, PPAYLOAD_HELLO)); - } - - private void sendResync() throws IOException - { - ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLEN_RESYNC]).order(ByteOrder.LITTLE_ENDIAN); - - conf.putLong(0); - conf.putLong(0xFFFF); - - sendAndGetReply(new NvCtlPacket(PTYPE_RESYNC, PPAYLEN_RESYNC, conf.array())); - } - - 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 - { - int bytesRead = in.read(header, offset, header.length - offset); - if (bytesRead < 0) { - break; - } - offset += bytesRead; - } while (offset != header.length); - - if (offset != header.length) { - throw new IOException("Socket closed prematurely"); - } - - 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 - { - int bytesRead = in.read(payload, offset, payload.length - offset); - if (bytesRead < 0) { - break; - } - offset += bytesRead; - } while (offset != payload.length); - - if (offset != payload.length) { - throw new IOException("Socket closed prematurely"); - } - } - } - - 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; - } - } - - @Override - public void connectionTerminated() { - abort(); - } - - @Override - public void connectionNeedsResync() { - synchronized (resyncNeeded) { - // Wake up the resync thread - resyncNeeded.notify(); - } - } -} diff --git a/src/com/limelight/nvstream/NvHTTP.java b/src/com/limelight/nvstream/NvHTTP.java deleted file mode 100644 index 65757e32..00000000 --- a/src/com/limelight/nvstream/NvHTTP.java +++ /dev/null @@ -1,147 +0,0 @@ -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; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -public class NvHTTP { - private String macAddress; - - public static final int PORT = 47989; - - public static final int CONNECTION_TIMEOUT = 5000; - - - public String baseUrl; - - public NvHTTP(InetAddress host, String macAddress) { - this.macAddress = macAddress; - this.baseUrl = "http://" + host.getHostAddress() + ":" + PORT; - } - - private String getXmlString(InputStream in, String tagname) - throws XmlPullParserException, IOException { - XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); - factory.setNamespaceAware(true); - XmlPullParser xpp = factory.newPullParser(); - - xpp.setInput(new InputStreamReader(in)); - int eventType = xpp.getEventType(); - Stack currentTag = new Stack(); - - while (eventType != XmlPullParser.END_DOCUMENT) { - switch (eventType) { - case (XmlPullParser.START_TAG): - currentTag.push(xpp.getName()); - break; - case (XmlPullParser.END_TAG): - currentTag.pop(); - break; - case (XmlPullParser.TEXT): - if (currentTag.peek().equals(tagname)) { - return xpp.getText(); - } - break; - } - eventType = xpp.next(); - } - - return null; - } - - private InputStream openHttpConnection(String url) throws IOException { - URLConnection conn = new URL(url).openConnection(); - conn.setConnectTimeout(CONNECTION_TIMEOUT); - conn.setDefaultUseCaches(false); - conn.connect(); - return conn.getInputStream(); - } - - public String getAppVersion() throws XmlPullParserException, IOException { - InputStream in = openHttpConnection(baseUrl + "/appversion"); - return getXmlString(in, "appversion"); - } - - public boolean getPairState() throws IOException, XmlPullParserException { - InputStream in = openHttpConnection(baseUrl + "/pairstate?mac=" + macAddress); - String paired = getXmlString(in, "paired"); - return Integer.valueOf(paired) != 0; - } - - public int getSessionId() throws IOException, XmlPullParserException { - /* Pass the model (minus spaces) as the device name */ - String deviceName = android.os.Build.MODEL; - deviceName = deviceName.replace(" ", ""); - InputStream in = openHttpConnection(baseUrl + "/pair?mac=" + macAddress - + "&devicename=" + deviceName); - String sessionId = getXmlString(in, "sessionid"); - return Integer.parseInt(sessionId); - } - - public int getSteamAppId(int sessionId) throws IOException, - XmlPullParserException { - LinkedList appList = getAppList(sessionId); - for (NvApp app : appList) { - if (app.getAppName().equals("Steam")) { - return app.getAppId(); - } - } - return 0; - } - - public LinkedList getAppList(int sessionId) throws IOException, XmlPullParserException { - InputStream in = openHttpConnection(baseUrl + "/applist?session=" + sessionId); - XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); - factory.setNamespaceAware(true); - XmlPullParser xpp = factory.newPullParser(); - - xpp.setInput(new InputStreamReader(in)); - int eventType = xpp.getEventType(); - LinkedList appList = new LinkedList(); - Stack currentTag = new Stack(); - - while (eventType != XmlPullParser.END_DOCUMENT) { - switch (eventType) { - case (XmlPullParser.START_TAG): - currentTag.push(xpp.getName()); - if (xpp.getName().equals("App")) { - appList.addLast(new NvApp()); - } - break; - case (XmlPullParser.END_TAG): - currentTag.pop(); - break; - case (XmlPullParser.TEXT): - NvApp app = appList.getLast(); - if (currentTag.peek().equals("AppTitle")) { - app.setAppName(xpp.getText()); - } else if (currentTag.peek().equals("ID")) { - app.setAppId(xpp.getText()); - } else if (currentTag.peek().equals("IsRunning")) { - app.setIsRunning(xpp.getText()); - } - break; - } - eventType = xpp.next(); - } - return appList; - } - - // Returns gameSession XML attribute - public int launchApp(int sessionId, int appId) throws IOException, - XmlPullParserException { - InputStream in = openHttpConnection(baseUrl + "/launch?session=" - + sessionId + "&appid=" + appId); - String gameSession = getXmlString(in, "gamesession"); - return Integer.parseInt(gameSession); - } -} diff --git a/src/com/limelight/nvstream/NvHandshake.java b/src/com/limelight/nvstream/NvHandshake.java deleted file mode 100644 index db111d16..00000000 --- a/src/com/limelight/nvstream/NvHandshake.java +++ /dev/null @@ -1,133 +0,0 @@ -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; - -public class NvHandshake { - public static final int PORT = 47991; - - public static final int HANDSHAKE_TIMEOUT = 5000; - - public static final byte[] PLATFORM_HELLO = - { - (byte)0x07, - (byte)0x00, - (byte)0x00, - (byte)0x00, - - // android in ASCII - (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 boolean waitAndDiscardResponse(InputStream in) - { - // Wait for response and discard response - try { - 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 boolean performHandshake(InetAddress host) throws IOException - { - 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(PLATFORM_HELLO); - out.flush(); - - if (!waitAndDiscardResponse(in)) { - s.close(); - return false; - } - - // Second packet - out.write(PACKET_2); - out.flush(); - - if (!waitAndDiscardResponse(in)) { - s.close(); - return false; - } - - // Third packet - out.write(PACKET_3); - out.flush(); - - if (!waitAndDiscardResponse(in)) { - s.close(); - return false; - } - - // Fourth packet - out.write(PACKET_4); - out.flush(); - - // Done - s.close(); - - return true; - } -} diff --git a/src/com/limelight/nvstream/NvVideoStream.java b/src/com/limelight/nvstream/NvVideoStream.java deleted file mode 100644 index 801bca52..00000000 --- a/src/com/limelight/nvstream/NvVideoStream.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.limelight.nvstream; - -import java.io.IOException; -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.util.LinkedList; -import java.util.concurrent.LinkedBlockingQueue; - -import com.limelight.nvstream.av.AvByteBufferDescriptor; -import com.limelight.nvstream.av.AvDecodeUnit; -import com.limelight.nvstream.av.AvRtpPacket; -import com.limelight.nvstream.av.ConnectionStatusListener; -import com.limelight.nvstream.av.video.AvVideoDepacketizer; -import com.limelight.nvstream.av.video.AvVideoPacket; -import com.limelight.nvstream.av.video.DecoderRenderer; -import com.limelight.nvstream.av.video.MediaCodecDecoderRenderer; -import com.limelight.nvstream.av.video.cpu.CpuDecoderRenderer; - -import android.content.Context; -import android.os.Build; -import android.view.SurfaceHolder; - -public class NvVideoStream { - public static final int RTP_PORT = 47998; - public static final int RTCP_PORT = 47999; - public static final int FIRST_FRAME_PORT = 47996; - - public static final int FIRST_FRAME_TIMEOUT = 5000; - - private LinkedBlockingQueue packets = new LinkedBlockingQueue(100); - - private InetAddress host; - private DatagramSocket rtp; - private Socket firstFrameSocket; - - private LinkedList threads = new LinkedList(); - - private NvConnectionListener listener; - private AvVideoDepacketizer depacketizer; - - private DecoderRenderer decrend; - private boolean startedRendering; - - private boolean aborting = false; - - public NvVideoStream(InetAddress host, NvConnectionListener listener, ConnectionStatusListener avConnListener) - { - this.host = host; - this.listener = listener; - this.depacketizer = new AvVideoDepacketizer(avConnListener); - } - - public void abort() - { - if (aborting) { - return; - } - - aborting = true; - - // Interrupt threads - for (Thread t : threads) { - t.interrupt(); - } - - // Close the socket to interrupt the receive thread - if (rtp != null) { - rtp.close(); - } - if (firstFrameSocket != null) { - try { - firstFrameSocket.close(); - } catch (IOException e) {} - } - - // Wait for threads to terminate - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException e) { } - } - - if (startedRendering) { - decrend.stop(); - } - - if (decrend != null) { - decrend.release(); - } - - threads.clear(); - } - - private void readFirstFrame() throws IOException - { - byte[] firstFrame = new byte[1500]; - - 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; - for (;;) - { - int bytesRead = firstFrameStream.read(firstFrame, offset, firstFrame.length-offset); - - if (bytesRead == -1) - break; - - offset += bytesRead; - } - - depacketizer.addInputData(new AvVideoPacket(new AvByteBufferDescriptor(firstFrame, 0, offset))); - } finally { - firstFrameSocket.close(); - firstFrameSocket = null; - } - } - - public void setupRtpSession() throws SocketException - { - rtp = new DatagramSocket(RTP_PORT); - } - - public void setupDecoderRenderer(Context context, SurfaceHolder renderTarget, int drFlags) { - if (Build.HARDWARE.equals("goldfish")) { - // Emulator - don't render video (it's slow!) - decrend = null; - } - else if (MediaCodecDecoderRenderer.findSafeDecoder() != null) { - // Hardware decoding - decrend = new MediaCodecDecoderRenderer(); - } - else { - // Software decoding - decrend = new CpuDecoderRenderer(); - } - - if (decrend != null) { - decrend.setup(context, 1280, 720, renderTarget, drFlags); - } - } - - public void startVideoStream(Context context, SurfaceHolder surface, int drFlags) throws IOException - { - // Setup the decoder and renderer - setupDecoderRenderer(context, surface, drFlags); - - // 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() - { - Thread t = new Thread() { - @Override - public void run() { - // Read the decode units generated from the RTP stream - while (!isInterrupted()) - { - AvDecodeUnit du; - - try { - du = depacketizer.getNextDecodeUnit(); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - - decrend.submitDecodeUnit(du); - } - } - }; - threads.add(t); - t.setName("Video - Decoder"); - t.setPriority(Thread.MAX_PRIORITY); - t.start(); - } - - private void startDepacketizerThread() - { - // This thread lessens the work on the receive thread - // so it can spend more time waiting for data - Thread t = new Thread() { - @Override - public void run() { - while (!isInterrupted()) - { - AvRtpPacket packet; - - try { - packet = packets.take(); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - - // !!! We no longer own the data buffer at this point !!! - depacketizer.addInputData(packet); - } - } - }; - threads.add(t); - t.setName("Video - Depacketizer"); - t.start(); - } - - private void startReceiveThread() - { - // Receive thread - Thread t = new Thread() { - @Override - public void run() { - AvByteBufferDescriptor desc = new AvByteBufferDescriptor(new byte[1500], 0, 1500); - DatagramPacket packet = new DatagramPacket(desc.data, desc.length); - - while (!isInterrupted()) - { - try { - rtp.receive(packet); - } catch (IOException e) { - listener.connectionTerminated(e); - return; - } - - // Give the packet to the depacketizer thread - desc.length = packet.getLength(); - if (packets.offer(new AvRtpPacket(desc))) { - desc.reinitialize(new byte[1500], 0, 1500); - packet.setData(desc.data, desc.offset, desc.length); - } - } - } - }; - threads.add(t); - t.setName("Video - Receive"); - t.start(); - } - - private void startUdpPingThread() - { - // Ping thread - Thread t = new Thread() { - @Override - public void run() { - // PING in ASCII - final byte[] pingPacketData = new byte[] {0x50, 0x49, 0x4E, 0x47}; - DatagramPacket pingPacket = new DatagramPacket(pingPacketData, pingPacketData.length); - pingPacket.setSocketAddress(new InetSocketAddress(host, RTP_PORT)); - - // Send PING every 100 ms - while (!isInterrupted()) - { - try { - rtp.send(pingPacket); - } catch (IOException e) { - listener.connectionTerminated(e); - return; - } - - try { - Thread.sleep(100); - } catch (InterruptedException e) { - listener.connectionTerminated(e); - return; - } - } - } - }; - threads.add(t); - t.setName("Video - Ping"); - t.setPriority(Thread.MIN_PRIORITY); - t.start(); - } -} diff --git a/src/com/limelight/nvstream/NvmDNS.java b/src/com/limelight/nvstream/NvmDNS.java deleted file mode 100644 index 65a5b2a4..00000000 --- a/src/com/limelight/nvstream/NvmDNS.java +++ /dev/null @@ -1,282 +0,0 @@ -package com.limelight.nvstream; - -import java.io.IOException; - -import java.net.DatagramPacket; -import java.net.InetAddress; -import java.net.MulticastSocket; - -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -import org.xbill.DNS.DClass; -import org.xbill.DNS.Header; -import org.xbill.DNS.Message; -import org.xbill.DNS.Name; -import org.xbill.DNS.Record; -import org.xbill.DNS.Section; -import org.xbill.DNS.TXTRecord; -import org.xbill.DNS.TextParseException; -import org.xbill.DNS.Type; - -import android.os.AsyncTask; -import android.util.Log; - -public class NvmDNS extends AsyncTask { - - public static String MDNS_QUERY = "_nvstream._tcp.local."; - public static String MDNS_MULTICAST_GROUP = "224.0.0.251"; - public static InetAddress MDNS_MULTICAST_ADDRESS; - public static final short MDNS_PORT = 5353; - - public static final int WAIT_MS = 1000; - - private HashSet responses; - private MulticastSocket socket; - - static { - try { - MDNS_MULTICAST_ADDRESS = InetAddress.getByName(NvmDNS.MDNS_MULTICAST_GROUP); - } catch (UnknownHostException e) { - MDNS_MULTICAST_ADDRESS = null; - } - } - - public NvmDNS() { - this.responses = new HashSet(); - - // Create our Socket Connection - try { - this.socket = new MulticastSocket(NvmDNS.MDNS_PORT); - this.socket.setLoopbackMode(false); - this.socket.joinGroup(NvmDNS.MDNS_MULTICAST_ADDRESS); - Log.v("NvmDNS Socket Constructor", "Created mDNS listening socket"); - } catch (IOException e) { - Log.e("NvmDNS Socket Constructor", "There was an error creating the DNS socket."); - Log.e("NvmDNS Socket Constructor", e.getMessage()); - } - } - - public Set getComputers() { - return Collections.unmodifiableSet(this.responses); - } - - private void sendQuery() { - Header queryHeader = new Header(); - - // If we set the RA (Recursion Available) flag and our message ID to 0 - // then the packet matches the real mDNS query packet as displayed in Wireshark - queryHeader.setFlag(org.xbill.DNS.Flags.RA); - queryHeader.setID(0); - - Record question = null; - try { - // We need to create our "Question" DNS query that is a pointer record to - // the mDNS Query "Name" - question = Record.newRecord(new Name(NvmDNS.MDNS_QUERY), Type.PTR, DClass.IN); - } catch (TextParseException e) { - Log.e("NvmDNS Query", e.getMessage()); - return; - } - - // We combine our header and our question into a single message - Message query = new Message(); - query.setHeader(queryHeader); - query.addRecord(question, Section.QUESTION); - - // Convert the message into Network Byte Order - byte[] wireQuery = query.toWire(); - Log.i("NvmDNS Query", query.toString()); - - // Convert our byte array into a Packet - DatagramPacket transmitPacket = new DatagramPacket(wireQuery, wireQuery.length); - transmitPacket.setAddress(NvmDNS.MDNS_MULTICAST_ADDRESS); - transmitPacket.setPort(NvmDNS.MDNS_PORT); - - // And (attempt) to send the packet - try { - Log.d("NvmDNS Query", "Blocking on this.nvstream_socket.send(transmitPacket)"); - this.socket.send(transmitPacket); - Log.d("NvmDNS Query", "Passed this.nvstream_socket.send(transmitPacket)"); - } catch (IOException e) { - Log.e("NvmDNS Query", "There was an error sending the DNS query."); - Log.e("NvmDNS Query", e.getMessage()); - } - } - - public void waitForResponses() { - Log.v("NvmDNS Response", "mDNS Loop Started"); - - // We support up to 1500 byte packets - byte[] data = new byte[1500]; - DatagramPacket packet = new DatagramPacket(data, data.length); - - Message message = null; - - while (!this.socket.isClosed()) { - // Attempt to receive a packet/response - try { - Log.d("NvmDNS Response", "Blocking on this.nvstream_query_socket.recieve()"); - this.socket.receive(packet); - Log.d("NvmDNS Response", "Blocking passed on this.nvstream_query_socket.recieve()"); - message = new Message(packet.getData()); - this.parseRecord(message, packet.getAddress()); - } catch (IOException e) { - if (this.socket.isClosed()) { - Log.e("NvmDNS Response", "The socket was closed on us. The timer must have been reached."); - return; - } else { - Log.e("NvmDNS Response", "There was an error receiving the response."); - Log.e("NvmDNS Response", e.getMessage()); - continue; - } - } - } - } - - private void parseRecord(Message message, InetAddress address) { - // We really only care about the ADDITIONAL section (specifically the text records) - Record[] responses = message.getSectionArray(Section.ADDITIONAL); - // We only want to process records that actually have a length, have an ANSWER - // section that has stuff in it and that the ANSWER to our query is what we sent - if (responses.length != 0 && - message.getSectionArray(Section.ANSWER).length != 0 && - message.getSectionArray(Section.ANSWER)[0].getName().toString().equals(NvmDNS.MDNS_QUERY)) { - - Log.v("NvmDNS Response", "Got a packet from " + address.getCanonicalHostName()); - Log.v("NvmDNS Response", "Question: " + message.getSectionArray(Section.ANSWER)[0].getName().toString()); - Log.v("NvmDNS Response", "Response: " + responses[0].getName().toString()); - - - // TODO: The DNS entry we get is "XENITH._nvstream._tcp.local." - // And the .'s in there are not actually periods. Or something. - String hostname = responses[0].getName().toString(); - - // The records can be returned in any order, so we need to figure out which one is the TXTRecord - // We get three records back: A TXTRecord, a SRVRecord and an ARecord - TXTRecord txtRecord = null; - - for (Record record : responses) { - Log.v("NvmDNS Response", "We recieved a DNS repsonse with a " + record.getClass().getName() + " record."); - if (record instanceof TXTRecord) { - txtRecord = (TXTRecord)record; - } - } - - if (txtRecord == null) { - Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with no TXTRecord"); - return; - } - - this.parseTXTRecord(txtRecord, address, hostname); - } - } - - private void parseTXTRecord(TXTRecord txtRecord, InetAddress address, String hostname) { - // The DNS library we are using does not use inferred generics :( - @SuppressWarnings("unchecked") - ArrayList txtRecordStringList = new ArrayList(txtRecord.getStrings()); - - if (txtRecordStringList.size() != 5) { - Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with the improper amount of TXTRecord Entries."); - return; - } - - // The general format of the text records is: - // SERVICE_STATE=1 - // SERVICE_NUMOFAPPS=5 - // SERVICE_GPUTYPE=GeForce GTX 760 x2 - // SERVICE_MAC=DE:AD:BE:EF:CA:FE - // SERVICE_UNIQUEID={A Wild UUID Appeared!} - // Every single record I've seen so far has been in this format - try { - int serviceState = Integer.parseInt(this.parseTXTRecordField(txtRecordStringList.get(0), "SERVICE_STATE")); - int numberOfApps = Integer.parseInt(this.parseTXTRecordField(txtRecordStringList.get(1), "SERVICE_NUMOFAPPS")); - String gpuType = this.parseTXTRecordField(txtRecordStringList.get(2), "SERVICE_GPUTYPE"); - String mac = this.parseTXTRecordField(txtRecordStringList.get(3), "SERVICE_MAC"); - UUID uniqueID = UUID.fromString(this.parseTXTRecordField(txtRecordStringList.get(4), "SERVICE_UNIQUEID")); - - // We need to resolve the hostname in this thread so that we can use it in the GUI - address.getCanonicalHostName(); - - NvComputer computer = new NvComputer(hostname, address, serviceState, numberOfApps, gpuType, mac, uniqueID); - this.responses.add(computer); - } catch (ArrayIndexOutOfBoundsException e) { - Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse."); - } - } - - private String parseTXTRecordField(String field, String key) { - // Make sure that our key=value pair actually has our key in it - if (!field.contains(key)) { - return ""; - } - - // Make sure that our key=value pair only has one "=" in it. - if (field.indexOf('=') != field.lastIndexOf('=')) { - return ""; - } - - String[] split = field.split("="); - - if (split.length != 2) { - return ""; - } - - return split[1]; - } - - // What follows is an implementation of Android's AsyncTask. - // The first step is to send our query, then we start our - // RX thread to parse responses. However we only want to accept - // responses for a limited amount of time so we start a new thread - // to kill the socket after a set amount of time - // Then we return control to the foreground thread - - @Override - protected Void doInBackground(Void... thisParameterIsUseless) { - Log.v("NvmDNS ASync", "doInBackground entered"); - - this.sendQuery(); - - - // We want to run our wait thread for an amount of time then close the socket. - new Thread(new Runnable() { - @Override - public void run() { - Log.v("NvmDNS Wait", "Going to sleep for " + NvmDNS.WAIT_MS + "ms"); - try { - Thread.sleep(NvmDNS.WAIT_MS); - } catch (InterruptedException e) { - Log.e("NvmDNS Wait", "Woke up from sleep before time."); - Log.e("NvmDNS Wait", e.getMessage()); - } - Log.v("NvmDNS Wait", "Woke up from sleep"); - NvmDNS.this.socket.close(); - Log.v("NvmDNS Wait", "Socket Closed"); - } - }).start(); - - this.waitForResponses(); - - Log.v("NvmDNS ASync", "doInBackground exit"); - return null; - } - - @Override - protected void onProgressUpdate(Integer... progress) { - Log.v("NvmDNS ASync", "onProgressUpdate"); - } - - @Override - protected void onPostExecute(Void moreUselessParameters) { - Log.v("NvmDNS ASync", "onPostExecute"); - for (NvComputer computer : this.responses) { - Log.i("NvmDNS NvComputer", computer.toString()); - } - } -} diff --git a/src/com/limelight/nvstream/av/AvByteBufferDescriptor.java b/src/com/limelight/nvstream/av/AvByteBufferDescriptor.java deleted file mode 100644 index 8f11a956..00000000 --- a/src/com/limelight/nvstream/av/AvByteBufferDescriptor.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.limelight.nvstream.av; - -public class AvByteBufferDescriptor { - public byte[] data; - public int offset; - public int length; - - public AvByteBufferDescriptor(byte[] data, int offset, int length) - { - this.data = data; - this.offset = offset; - this.length = length; - } - - public AvByteBufferDescriptor(AvByteBufferDescriptor desc) - { - this.data = desc.data; - this.offset = desc.offset; - this.length = desc.length; - } - - public void reinitialize(byte[] data, int offset, int length) - { - this.data = data; - this.offset = offset; - this.length = length; - } - - public void print() - { - print(offset, length); - } - - public void print(int length) - { - print(this.offset, length); - } - - public void print(int offset, int length) - { - for (int i = offset; i < offset+length; i++) { - System.out.printf("%d: %02x \n", i, data[i]); - } - System.out.println(); - } -} diff --git a/src/com/limelight/nvstream/av/AvDecodeUnit.java b/src/com/limelight/nvstream/av/AvDecodeUnit.java deleted file mode 100644 index 69300b83..00000000 --- a/src/com/limelight/nvstream/av/AvDecodeUnit.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.limelight.nvstream.av; - -import java.util.List; - -public class AvDecodeUnit { - public static final int TYPE_UNKNOWN = 0; - public static final int TYPE_H264 = 1; - public static final int TYPE_OPUS = 2; - - private int type; - private List bufferList; - private int dataLength; - private int flags; - - public AvDecodeUnit(int type, List bufferList, int dataLength, int flags) - { - this.type = type; - this.bufferList = bufferList; - this.dataLength = dataLength; - this.flags = flags; - } - - public int getType() - { - return type; - } - - public int getFlags() - { - return flags; - } - - public List getBufferList() - { - return bufferList; - } - - public int getDataLength() - { - return dataLength; - } -} diff --git a/src/com/limelight/nvstream/av/AvRtpPacket.java b/src/com/limelight/nvstream/av/AvRtpPacket.java deleted file mode 100644 index 8e4250e7..00000000 --- a/src/com/limelight/nvstream/av/AvRtpPacket.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.limelight.nvstream.av; - -import java.nio.ByteBuffer; - -public class AvRtpPacket { - - private byte packetType; - private short seqNum; - private AvByteBufferDescriptor buffer; - - public AvRtpPacket(AvByteBufferDescriptor buffer) - { - this.buffer = new AvByteBufferDescriptor(buffer); - - ByteBuffer bb = ByteBuffer.wrap(buffer.data, buffer.offset, buffer.length); - - // Discard the first byte - bb.position(bb.position()+1); - - // Get the packet type - packetType = bb.get(); - - // Get the sequence number - seqNum = bb.getShort(); - } - - public byte getPacketType() - { - return packetType; - } - - public short getSequenceNumber() - { - return seqNum; - } - - public byte[] getBackingBuffer() - { - return buffer.data; - } - - public AvByteBufferDescriptor getNewPayloadDescriptor() - { - return new AvByteBufferDescriptor(buffer.data, buffer.offset+12, buffer.length-12); - } -} diff --git a/src/com/limelight/nvstream/av/AvShortBufferDescriptor.java b/src/com/limelight/nvstream/av/AvShortBufferDescriptor.java deleted file mode 100644 index 901783f4..00000000 --- a/src/com/limelight/nvstream/av/AvShortBufferDescriptor.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.limelight.nvstream.av; - -public class AvShortBufferDescriptor { - public short[] data; - public int offset; - public int length; - - public AvShortBufferDescriptor(short[] data, int offset, int length) - { - this.data = data; - this.offset = offset; - this.length = length; - } - - public AvShortBufferDescriptor(AvShortBufferDescriptor desc) - { - this.data = desc.data; - this.offset = desc.offset; - this.length = desc.length; - } - - public void reinitialize(short[] data, int offset, int length) - { - this.data = data; - this.offset = offset; - this.length = length; - } -} diff --git a/src/com/limelight/nvstream/av/ConnectionStatusListener.java b/src/com/limelight/nvstream/av/ConnectionStatusListener.java deleted file mode 100644 index 35262ddf..00000000 --- a/src/com/limelight/nvstream/av/ConnectionStatusListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.limelight.nvstream.av; - -public interface ConnectionStatusListener { - public void connectionTerminated(); - - public void connectionNeedsResync(); -} diff --git a/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java b/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java deleted file mode 100644 index a13cf3ef..00000000 --- a/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.limelight.nvstream.av.audio; - -import java.util.concurrent.LinkedBlockingQueue; - -import com.limelight.nvstream.av.AvByteBufferDescriptor; -import com.limelight.nvstream.av.AvRtpPacket; -import com.limelight.nvstream.av.AvShortBufferDescriptor; - -public class AvAudioDepacketizer { - - private static final int DU_LIMIT = 15; - private LinkedBlockingQueue decodedUnits = - new LinkedBlockingQueue(DU_LIMIT); - - // Sequencing state - private short lastSequenceNumber; - - private void decodeData(byte[] data, int off, int len) - { - // Submit this data to the decoder - short[] pcmData = new short[OpusDecoder.getMaxOutputShorts()]; - int decodeLen = OpusDecoder.decode(data, off, len, pcmData); - - if (decodeLen > 0) { - // Return value of decode is frames decoded per channel - decodeLen *= OpusDecoder.getChannelCount(); - - // Put it on the decoded queue - if (!decodedUnits.offer(new AvShortBufferDescriptor(pcmData, 0, decodeLen))) { - // Clear out the queue - decodedUnits.clear(); - } - } - } - - public void decodeInputData(AvRtpPacket packet) - { - short seq = packet.getSequenceNumber(); - - if (packet.getPacketType() != 97) { - // Only type 97 is audio - return; - } - - // Toss out the current NAL if we receive a packet that is - // out of sequence - if (lastSequenceNumber != 0 && - (short)(lastSequenceNumber + 1) != seq) - { - System.out.println("Received OOS audio data (expected "+(lastSequenceNumber + 1)+", got "+seq+")"); - decodeData(null, 0, 0); - } - - lastSequenceNumber = seq; - - // This is all the depacketizing we need to do - AvByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor(); - decodeData(rtpPayload.data, rtpPayload.offset, rtpPayload.length); - } - - public AvShortBufferDescriptor getNextDecodedData() throws InterruptedException - { - return decodedUnits.take(); - } -} diff --git a/src/com/limelight/nvstream/av/audio/OpusDecoder.java b/src/com/limelight/nvstream/av/audio/OpusDecoder.java deleted file mode 100644 index c01f7fa5..00000000 --- a/src/com/limelight/nvstream/av/audio/OpusDecoder.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.limelight.nvstream.av.audio; - -public class OpusDecoder { - static { - System.loadLibrary("nv_opus_dec"); - } - - public static native int init(); - public static native void destroy(); - public static native int getChannelCount(); - public static native int getMaxOutputShorts(); - public static native int getSampleRate(); - public static native int decode(byte[] indata, int inoff, int inlen, short[] outpcmdata); -} diff --git a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java deleted file mode 100644 index d6867d70..00000000 --- a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java +++ /dev/null @@ -1,213 +0,0 @@ -package com.limelight.nvstream.av.video; - -import java.util.LinkedList; -import java.util.concurrent.LinkedBlockingQueue; - -import com.limelight.nvstream.av.AvByteBufferDescriptor; -import com.limelight.nvstream.av.AvDecodeUnit; -import com.limelight.nvstream.av.AvRtpPacket; -import com.limelight.nvstream.av.ConnectionStatusListener; - -public class AvVideoDepacketizer { - - // Current NAL state - private LinkedList avcNalDataChain = null; - private int avcNalDataLength = 0; - - // Sequencing state - private short lastSequenceNumber; - - private ConnectionStatusListener controlListener; - - private static final int DU_LIMIT = 15; - private LinkedBlockingQueue decodedUnits = new LinkedBlockingQueue(DU_LIMIT); - - public AvVideoDepacketizer(ConnectionStatusListener controlListener) - { - this.controlListener = controlListener; - } - - private void clearAvcNalState() - { - avcNalDataChain = null; - avcNalDataLength = 0; - } - - private void reassembleAvcNal() - { - // This is the start of a new NAL - if (avcNalDataChain != null && avcNalDataLength != 0) { - // Construct the H264 decode unit - AvDecodeUnit du = new AvDecodeUnit(AvDecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, 0); - if (!decodedUnits.offer(du)) { - // We need a new IDR frame since we're discarding data now - decodedUnits.clear(); - controlListener.connectionNeedsResync(); - } - - // Clear old state - clearAvcNalState(); - } - } - - public void addInputData(AvVideoPacket packet) - { - AvByteBufferDescriptor location = packet.getNewPayloadDescriptor(); - - // SPS and PPS packet doesn't have standard headers, so submit it as is - if (location.length < 968) { - avcNalDataChain = new LinkedList(); - avcNalDataLength = 0; - - avcNalDataChain.add(location); - avcNalDataLength += location.length; - - reassembleAvcNal(); - } - else { - int packetIndex = packet.getPacketIndex(); - int packetsInFrame = packet.getTotalPackets(); - - // Check if this is the first packet for a frame - if (packetIndex == 0) { - // Setup state for the new frame - avcNalDataChain = new LinkedList(); - avcNalDataLength = 0; - } - - // Check if this packet falls in the range of packets in frame - if (packetIndex >= packetsInFrame) { - // This isn't H264 frame data - return; - } - - // Adjust the length to only contain valid data - location.length = packet.getPayloadLength(); - - // Add the payload data to the chain - if (avcNalDataChain != null) { - avcNalDataChain.add(location); - avcNalDataLength += location.length; - } - - // Reassemble the NALs if this was the last packet for this frame - if (packetIndex + 1 == packetsInFrame) { - reassembleAvcNal(); - } - } - } - - public void addInputData(AvRtpPacket packet) - { - short seq = packet.getSequenceNumber(); - - // Toss out the current NAL if we receive a packet that is - // out of sequence - if (lastSequenceNumber != 0 && - (short)(lastSequenceNumber + 1) != seq) - { - System.out.println("Received OOS video data (expected "+(lastSequenceNumber + 1)+", got "+seq+")"); - - // Reset the depacketizer state - clearAvcNalState(); - - // Request an IDR frame - controlListener.connectionNeedsResync(); - } - - lastSequenceNumber = seq; - - // Pass the payload to the non-sequencing parser - AvByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor(); - addInputData(new AvVideoPacket(rtpPayload)); - } - - public AvDecodeUnit getNextDecodeUnit() throws InterruptedException - { - return decodedUnits.take(); - } -} - -class NAL { - - // This assumes that the buffer passed in is already a special sequence - public static boolean isAvcStartSequence(AvByteBufferDescriptor specialSeq) - { - // The start sequence is 00 00 01 or 00 00 00 01 - return (specialSeq.data[specialSeq.offset+specialSeq.length-1] == 0x01); - } - - // This assumes that the buffer passed in is already a special sequence - public static boolean isAvcFrameStart(AvByteBufferDescriptor specialSeq) - { - if (specialSeq.length != 4) - return false; - - // The frame start sequence is 00 00 00 01 - return (specialSeq.data[specialSeq.offset+specialSeq.length-1] == 0x01); - } - - // Returns a buffer descriptor describing the start sequence - public static boolean getSpecialSequenceDescriptor(AvByteBufferDescriptor buffer, AvByteBufferDescriptor outputDesc) - { - // NAL start sequence is 00 00 00 01 or 00 00 01 - if (buffer.length < 3) - return false; - - // 00 00 is magic - if (buffer.data[buffer.offset] == 0x00 && - buffer.data[buffer.offset+1] == 0x00) - { - // Another 00 could be the end of the special sequence - // 00 00 00 or the middle of 00 00 00 01 - if (buffer.data[buffer.offset+2] == 0x00) - { - if (buffer.length >= 4 && - buffer.data[buffer.offset+3] == 0x01) - { - // It's the AVC start sequence 00 00 00 01 - outputDesc.reinitialize(buffer.data, buffer.offset, 4); - } - else - { - // It's 00 00 00 - outputDesc.reinitialize(buffer.data, buffer.offset, 3); - } - return true; - } - else if (buffer.data[buffer.offset+2] == 0x01 || - buffer.data[buffer.offset+2] == 0x02) - { - // These are easy: 00 00 01 or 00 00 02 - outputDesc.reinitialize(buffer.data, buffer.offset, 3); - return true; - } - else if (buffer.data[buffer.offset+2] == 0x03) - { - // 00 00 03 is special because it's a subsequence of the - // NAL wrapping substitute for 00 00 00, 00 00 01, 00 00 02, - // or 00 00 03 in the RBSP sequence. We need to check the next - // byte to see whether it's 00, 01, 02, or 03 (a valid RBSP substitution) - // or whether it's something else - - if (buffer.length < 4) - return false; - - if (buffer.data[buffer.offset+3] >= 0x00 && - buffer.data[buffer.offset+3] <= 0x03) - { - // It's not really a special sequence after all - return false; - } - else - { - // It's not a standard replacement so it's a special sequence - outputDesc.reinitialize(buffer.data, buffer.offset, 3); - return true; - } - } - } - - return false; - } -} diff --git a/src/com/limelight/nvstream/av/video/AvVideoPacket.java b/src/com/limelight/nvstream/av/video/AvVideoPacket.java deleted file mode 100644 index 0113f344..00000000 --- a/src/com/limelight/nvstream/av/video/AvVideoPacket.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.limelight.nvstream.av.video; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import com.limelight.nvstream.av.AvByteBufferDescriptor; - -public class AvVideoPacket { - private AvByteBufferDescriptor buffer; - - private int frameIndex; - private int packetIndex; - private int totalPackets; - private int payloadLength; - - public AvVideoPacket(AvByteBufferDescriptor rtpPayload) - { - buffer = new AvByteBufferDescriptor(rtpPayload); - - ByteBuffer bb = ByteBuffer.wrap(buffer.data).order(ByteOrder.LITTLE_ENDIAN); - bb.position(buffer.offset); - - frameIndex = bb.getInt(); - packetIndex = bb.getInt(); - totalPackets = bb.getInt(); - - bb.position(bb.position()+4); - - payloadLength = bb.getInt(); - } - - public int getFrameIndex() - { - return frameIndex; - } - - public int getPacketIndex() - { - return packetIndex; - } - - public int getPayloadLength() - { - return payloadLength; - } - - public int getTotalPackets() - { - return totalPackets; - } - - public AvByteBufferDescriptor getNewPayloadDescriptor() - { - return new AvByteBufferDescriptor(buffer.data, buffer.offset+56, buffer.length-56); - } -} diff --git a/src/com/limelight/nvstream/av/video/DecoderRenderer.java b/src/com/limelight/nvstream/av/video/DecoderRenderer.java deleted file mode 100644 index 486d0f18..00000000 --- a/src/com/limelight/nvstream/av/video/DecoderRenderer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.limelight.nvstream.av.video; - -import com.limelight.nvstream.av.AvDecodeUnit; - -import android.content.Context; -import android.view.SurfaceHolder; - -public interface DecoderRenderer { - public static int FLAG_PREFER_QUALITY = 0x1; - - public void setup(Context context, int width, int height, SurfaceHolder renderTarget, int drFlags); - - public void start(); - - public void stop(); - - public void release(); - - public boolean submitDecodeUnit(AvDecodeUnit decodeUnit); -} diff --git a/src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java b/src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java deleted file mode 100644 index d9cbcf31..00000000 --- a/src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.limelight.nvstream.av.video.cpu; - -import android.view.Surface; - -public class AvcDecoder { - static { - // FFMPEG dependencies - System.loadLibrary("avutil-52"); - System.loadLibrary("swresample-0"); - System.loadLibrary("swscale-2"); - System.loadLibrary("avcodec-55"); - System.loadLibrary("avformat-55"); - System.loadLibrary("avfilter-3"); - - System.loadLibrary("nv_avc_dec"); - } - - /** Disables the deblocking filter at the cost of image quality */ - public static final int DISABLE_LOOP_FILTER = 0x1; - /** Uses the low latency decode flag (disables multithreading) */ - public static final int LOW_LATENCY_DECODE = 0x2; - /** Threads process each slice, rather than each frame */ - public static final int SLICE_THREADING = 0x4; - /** Uses nonstandard speedup tricks */ - public static final int FAST_DECODE = 0x8; - /** Uses bilinear filtering instead of bicubic */ - public static final int BILINEAR_FILTERING = 0x10; - /** Uses a faster bilinear filtering with lower image quality */ - public static final int FAST_BILINEAR_FILTERING = 0x20; - /** Disables color conversion (output is NV21) */ - public static final int NO_COLOR_CONVERSION = 0x40; - - public static native int init(int width, int height, int perflvl, int threadcount); - public static native void destroy(); - - // Rendering API when NO_COLOR_CONVERSION == 0 - public static native boolean setRenderTarget(Surface surface); - public static native boolean getRgbFrame(byte[] rgbFrame, int bufferSize); - public static native boolean redraw(); - - // Rendering API when NO_COLOR_CONVERSION == 1 - public static native boolean getRawFrame(byte[] yuvFrame, int bufferSize); - - public static native int getInputPaddingSize(); - public static native int decode(byte[] indata, int inoff, int inlen); -} diff --git a/src/com/limelight/nvstream/input/NvController.java b/src/com/limelight/nvstream/input/NvController.java deleted file mode 100644 index b1b273e7..00000000 --- a/src/com/limelight/nvstream/input/NvController.java +++ /dev/null @@ -1,65 +0,0 @@ -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; - -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(InetAddress host) - { - 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(); - } - - public void close() - { - try { - s.close(); - } catch (IOException e) {} - } - - public void sendControllerInput(short buttonFlags, byte leftTrigger, byte rightTrigger, - short leftStickX, short leftStickY, short rightStickX, short rightStickY) throws IOException - { - out.write(new NvControllerPacket(buttonFlags, leftTrigger, - rightTrigger, leftStickX, leftStickY, - rightStickX, rightStickY).toWire()); - out.flush(); - } - - public void sendMouseButtonDown() throws IOException - { - out.write(new NvMouseButtonPacket(true).toWire()); - out.flush(); - } - - public void sendMouseButtonUp() throws IOException - { - out.write(new NvMouseButtonPacket(false).toWire()); - out.flush(); - } - - public void sendMouseMove(short deltaX, short deltaY) throws IOException - { - out.write(new NvMouseMovePacket(deltaX, deltaY).toWire()); - out.flush(); - } -} diff --git a/src/com/limelight/nvstream/input/NvControllerPacket.java b/src/com/limelight/nvstream/input/NvControllerPacket.java deleted file mode 100644 index 06ab0061..00000000 --- a/src/com/limelight/nvstream/input/NvControllerPacket.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.limelight.nvstream.input; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class NvControllerPacket extends NvInputPacket { - public static final byte[] HEADER = - { - 0x0A, - 0x00, - 0x00, - 0x00, - 0x00, - 0x14 - }; - - public static final byte[] TAIL = - { - (byte)0x9C, - 0x00, - 0x00, - 0x00, - 0x55, - 0x00 - }; - - public static final int PACKET_TYPE = 0x18; - - 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 LB_FLAG = 0x0100; - public static final short RB_FLAG = 0x0200; - public static final short PLAY_FLAG = 0x0010; - public static final short BACK_FLAG = 0x0020; - public static final short LS_CLK_FLAG = 0x0040; - public static final short RS_CLK_FLAG = 0x0080; - public static final short SPECIAL_BUTTON_FLAG = 0x0400; - - public static final short PAYLOAD_LENGTH = 24; - public static final short PACKET_LENGTH = PAYLOAD_LENGTH + - NvInputPacket.HEADER_LENGTH; - - private short buttonFlags; - private byte leftTrigger; - private byte rightTrigger; - private short leftStickX; - private short leftStickY; - private short rightStickX; - private short rightStickY; - - public NvControllerPacket(short buttonFlags, byte leftTrigger, byte rightTrigger, - short leftStickX, short leftStickY, - short rightStickX, short rightStickY) - { - super(PACKET_TYPE); - - this.buttonFlags = buttonFlags; - this.leftTrigger = leftTrigger; - this.rightTrigger = rightTrigger; - this.leftStickX = leftStickX; - this.leftStickY = leftStickY; - this.rightStickX = rightStickX; - this.rightStickY = rightStickY; - } - - public byte[] toWire() - { - ByteBuffer bb = ByteBuffer.allocate(PACKET_LENGTH).order(ByteOrder.LITTLE_ENDIAN); - - bb.put(toWireHeader()); - bb.put(HEADER); - bb.putShort(buttonFlags); - bb.put(leftTrigger); - bb.put(rightTrigger); - bb.putShort(leftStickX); - bb.putShort(leftStickY); - bb.putShort(rightStickX); - bb.putShort(rightStickY); - bb.put(TAIL); - - return bb.array(); - } - } \ No newline at end of file diff --git a/src/com/limelight/nvstream/input/NvInputPacket.java b/src/com/limelight/nvstream/input/NvInputPacket.java deleted file mode 100644 index ec98b2de..00000000 --- a/src/com/limelight/nvstream/input/NvInputPacket.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.limelight.nvstream.input; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public abstract class NvInputPacket { - public static final int HEADER_LENGTH = 0x4; - - protected int packetType; - - public NvInputPacket(int packetType) - { - this.packetType = packetType; - } - - public abstract byte[] toWire(); - - public byte[] toWireHeader() - { - ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN); - - bb.putInt(packetType); - - return bb.array(); - } -} diff --git a/src/com/limelight/nvstream/input/NvMouseButtonPacket.java b/src/com/limelight/nvstream/input/NvMouseButtonPacket.java deleted file mode 100644 index 8cb87ac7..00000000 --- a/src/com/limelight/nvstream/input/NvMouseButtonPacket.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.limelight.nvstream.input; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class NvMouseButtonPacket extends NvInputPacket { - - private byte buttonEventType; - - public static final int PACKET_TYPE = 0x5; - public static final int PAYLOAD_LENGTH = 5; - public static final int PACKET_LENGTH = PAYLOAD_LENGTH + - NvInputPacket.HEADER_LENGTH; - - public static final byte PRESS_EVENT = 0x07; - public static final byte RELEASE_EVENT = 0x08; - - public NvMouseButtonPacket(boolean leftButtonDown) - { - super(PACKET_TYPE); - - buttonEventType = leftButtonDown ? - PRESS_EVENT : RELEASE_EVENT; - } - - @Override - public byte[] toWire() { - ByteBuffer bb = ByteBuffer.allocate(PACKET_LENGTH).order(ByteOrder.BIG_ENDIAN); - - bb.put(toWireHeader()); - bb.put(buttonEventType); - bb.putInt(1); // FIXME: button index? - - return bb.array(); - } -} diff --git a/src/com/limelight/nvstream/input/NvMouseMovePacket.java b/src/com/limelight/nvstream/input/NvMouseMovePacket.java deleted file mode 100644 index edafa9c7..00000000 --- a/src/com/limelight/nvstream/input/NvMouseMovePacket.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.limelight.nvstream.input; - -import java.nio.ByteBuffer; - -public class NvMouseMovePacket extends NvInputPacket { - - private static final byte[] HEADER = - { - 0x06, - 0x00, - 0x00, - 0x00 - }; - - public static final int PACKET_TYPE = 0x8; - public static final int PAYLOAD_LENGTH = 8; - public static final int PACKET_LENGTH = PAYLOAD_LENGTH + - NvInputPacket.HEADER_LENGTH; - - private short deltaX; - private short deltaY; - - public NvMouseMovePacket(short deltaX, short deltaY) - { - super(PACKET_TYPE); - - this.deltaX = deltaX; - this.deltaY = deltaY; - } - - @Override - public byte[] toWire() { - ByteBuffer bb = ByteBuffer.allocate(PACKET_LENGTH); - - bb.put(toWireHeader()); - bb.put(HEADER); - bb.putShort(deltaX); - bb.putShort(deltaY); - - return bb.array(); - } -}