From 14d093cf069bce9ea7590d82b31debe9af58db25 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 5 Dec 2013 12:59:36 -0500 Subject: [PATCH] Remove code shared with limelight-common and implement Android bindings. --- .classpath | 1 + libs/limelight-common.jar | Bin 0 -> 166887 bytes src/com/limelight/Connection.java | 15 +- src/com/limelight/Game.java | 121 +++-- .../limelight/binding/PlatformBinding.java | 36 ++ .../binding/audio/AndroidAudioRenderer.java | 50 ++ .../video/AndroidCpuDecoderRenderer.java} | 41 +- .../video/MediaCodecDecoderRenderer.java | 23 +- .../cpu => binding/video}/RsRenderer.java | 2 +- src/com/limelight/nvstream/NvApp.java | 31 -- src/com/limelight/nvstream/NvAudioStream.java | 250 --------- src/com/limelight/nvstream/NvComputer.java | 164 ------ src/com/limelight/nvstream/NvConnection.java | 338 ------------ .../nvstream/NvConnectionListener.java | 30 -- src/com/limelight/nvstream/NvControl.java | 505 ------------------ src/com/limelight/nvstream/NvHTTP.java | 147 ----- src/com/limelight/nvstream/NvHandshake.java | 133 ----- src/com/limelight/nvstream/NvVideoStream.java | 310 ----------- src/com/limelight/nvstream/NvmDNS.java | 282 ---------- .../nvstream/av/AvByteBufferDescriptor.java | 46 -- .../limelight/nvstream/av/AvDecodeUnit.java | 42 -- .../limelight/nvstream/av/AvRtpPacket.java | 46 -- .../nvstream/av/AvShortBufferDescriptor.java | 28 - .../nvstream/av/ConnectionStatusListener.java | 7 - .../av/audio/AvAudioDepacketizer.java | 65 --- .../nvstream/av/audio/OpusDecoder.java | 14 - .../av/video/AvVideoDepacketizer.java | 213 -------- .../nvstream/av/video/AvVideoPacket.java | 56 -- .../nvstream/av/video/DecoderRenderer.java | 20 - .../nvstream/av/video/cpu/AvcDecoder.java | 46 -- .../nvstream/input/NvController.java | 65 --- .../nvstream/input/NvControllerPacket.java | 89 --- .../nvstream/input/NvInputPacket.java | 26 - .../nvstream/input/NvMouseButtonPacket.java | 36 -- .../nvstream/input/NvMouseMovePacket.java | 42 -- 35 files changed, 190 insertions(+), 3130 deletions(-) create mode 100644 libs/limelight-common.jar create mode 100644 src/com/limelight/binding/PlatformBinding.java create mode 100644 src/com/limelight/binding/audio/AndroidAudioRenderer.java rename src/com/limelight/{nvstream/av/video/cpu/CpuDecoderRenderer.java => binding/video/AndroidCpuDecoderRenderer.java} (80%) rename src/com/limelight/{nvstream/av => binding}/video/MediaCodecDecoderRenderer.java (85%) rename src/com/limelight/{nvstream/av/video/cpu => binding/video}/RsRenderer.java (95%) delete mode 100644 src/com/limelight/nvstream/NvApp.java delete mode 100644 src/com/limelight/nvstream/NvAudioStream.java delete mode 100644 src/com/limelight/nvstream/NvComputer.java delete mode 100644 src/com/limelight/nvstream/NvConnection.java delete mode 100644 src/com/limelight/nvstream/NvConnectionListener.java delete mode 100644 src/com/limelight/nvstream/NvControl.java delete mode 100644 src/com/limelight/nvstream/NvHTTP.java delete mode 100644 src/com/limelight/nvstream/NvHandshake.java delete mode 100644 src/com/limelight/nvstream/NvVideoStream.java delete mode 100644 src/com/limelight/nvstream/NvmDNS.java delete mode 100644 src/com/limelight/nvstream/av/AvByteBufferDescriptor.java delete mode 100644 src/com/limelight/nvstream/av/AvDecodeUnit.java delete mode 100644 src/com/limelight/nvstream/av/AvRtpPacket.java delete mode 100644 src/com/limelight/nvstream/av/AvShortBufferDescriptor.java delete mode 100644 src/com/limelight/nvstream/av/ConnectionStatusListener.java delete mode 100644 src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java delete mode 100644 src/com/limelight/nvstream/av/audio/OpusDecoder.java delete mode 100644 src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java delete mode 100644 src/com/limelight/nvstream/av/video/AvVideoPacket.java delete mode 100644 src/com/limelight/nvstream/av/video/DecoderRenderer.java delete mode 100644 src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java delete mode 100644 src/com/limelight/nvstream/input/NvController.java delete mode 100644 src/com/limelight/nvstream/input/NvControllerPacket.java delete mode 100644 src/com/limelight/nvstream/input/NvInputPacket.java delete mode 100644 src/com/limelight/nvstream/input/NvMouseButtonPacket.java delete mode 100644 src/com/limelight/nvstream/input/NvMouseMovePacket.java 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 0000000000000000000000000000000000000000..a6546181ba10b76ca4033f3412d671009e8b00c4 GIT binary patch literal 166887 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⪻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

C%@r; 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 af|WI_Y#;;l8T1)M1sNFjR539yFaQ8r>#Umq literal 0 HcmV?d00001 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(); - } -}