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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

e3;h=!CISY4va1N_N^3cB#SjR%dYW3kd|qO#su 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 e2>yENDlZKKDX)TpVupO-U_e1Nx#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(); - } -}