From a3d5e955aaba95c53a55571f24905d7368f575ba Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 14 May 2017 17:12:30 -0700 Subject: [PATCH] Rework interfaces for JNI bridge --- .../limelight/nvstream/ConnectionContext.java | 3 +- .../com/limelight/nvstream/NvConnection.java | 9 +- .../nvstream/NvConnectionListener.java | 4 +- .../com/limelight/nvstream/av/DecodeUnit.java | 50 -------- .../nvstream/av/audio/AudioRenderer.java | 11 +- .../av/video/VideoDecoderRenderer.java | 33 +----- .../limelight/nvstream/jni/MoonBridge.java | 110 ++++++++++++++++++ 7 files changed, 125 insertions(+), 95 deletions(-) delete mode 100644 moonlight-common/src/main/java/com/limelight/nvstream/av/DecodeUnit.java create mode 100644 moonlight-common/src/main/java/com/limelight/nvstream/jni/MoonBridge.java diff --git a/moonlight-common/src/main/java/com/limelight/nvstream/ConnectionContext.java b/moonlight-common/src/main/java/com/limelight/nvstream/ConnectionContext.java index b38717e2..952c72f7 100644 --- a/moonlight-common/src/main/java/com/limelight/nvstream/ConnectionContext.java +++ b/moonlight-common/src/main/java/com/limelight/nvstream/ConnectionContext.java @@ -5,7 +5,6 @@ import java.net.InetAddress; import javax.crypto.SecretKey; import com.limelight.nvstream.av.video.VideoDecoderRenderer; -import com.limelight.nvstream.av.video.VideoDecoderRenderer.VideoFormat; public class ConnectionContext { // Gen 3 servers are 2.1.1 - 2.2.1 @@ -35,7 +34,7 @@ public class ConnectionContext { // This is the version quad from the appversion tag of /serverinfo public int[] serverAppVersion; - public VideoFormat negotiatedVideoFormat; + public int negotiatedVideoFormat; public int negotiatedWidth, negotiatedHeight; public int negotiatedFps; } diff --git a/moonlight-common/src/main/java/com/limelight/nvstream/NvConnection.java b/moonlight-common/src/main/java/com/limelight/nvstream/NvConnection.java index 5c089724..0731bea3 100644 --- a/moonlight-common/src/main/java/com/limelight/nvstream/NvConnection.java +++ b/moonlight-common/src/main/java/com/limelight/nvstream/NvConnection.java @@ -14,7 +14,6 @@ import org.xmlpull.v1.XmlPullParserException; import com.limelight.LimeLog; import com.limelight.nvstream.av.audio.AudioRenderer; import com.limelight.nvstream.av.video.VideoDecoderRenderer; -import com.limelight.nvstream.av.video.VideoDecoderRenderer.VideoFormat; import com.limelight.nvstream.http.GfeHttpResponseException; import com.limelight.nvstream.http.LimelightCryptoProvider; import com.limelight.nvstream.http.NvApp; @@ -47,7 +46,7 @@ public class NvConnection { this.context.riKeyId = generateRiKeyId(); - this.context.negotiatedVideoFormat = VideoFormat.Unknown; + this.context.negotiatedVideoFormat = -1; } private static SecretKey generateRiAesKey() throws NoSuchAlgorithmException { @@ -249,7 +248,7 @@ public class NvConnection { } catch (Exception e) { e.printStackTrace(); context.connListener.displayMessage(e.getMessage()); - context.connListener.stageFailed(appName); + context.connListener.stageFailed(appName, 0); return; } } @@ -263,7 +262,7 @@ public class NvConnection { try { context.serverAddress = InetAddress.getByName(host); } catch (UnknownHostException e) { - context.connListener.connectionTerminated(e); + context.connListener.connectionTerminated(-1); return; } @@ -312,7 +311,7 @@ public class NvConnection { } - public VideoFormat getActiveVideoFormat() { + public int getActiveVideoFormat() { return context.negotiatedVideoFormat; } } diff --git a/moonlight-common/src/main/java/com/limelight/nvstream/NvConnectionListener.java b/moonlight-common/src/main/java/com/limelight/nvstream/NvConnectionListener.java index f432c5a1..bcb80d60 100644 --- a/moonlight-common/src/main/java/com/limelight/nvstream/NvConnectionListener.java +++ b/moonlight-common/src/main/java/com/limelight/nvstream/NvConnectionListener.java @@ -3,10 +3,10 @@ package com.limelight.nvstream; public interface NvConnectionListener { void stageStarting(String stage); void stageComplete(String stage); - void stageFailed(String stage); + void stageFailed(String stage, long errorCode); void connectionStarted(); - void connectionTerminated(Exception e); + void connectionTerminated(long errorCode); void displayMessage(String message); void displayTransientMessage(String message); diff --git a/moonlight-common/src/main/java/com/limelight/nvstream/av/DecodeUnit.java b/moonlight-common/src/main/java/com/limelight/nvstream/av/DecodeUnit.java deleted file mode 100644 index 78789b86..00000000 --- a/moonlight-common/src/main/java/com/limelight/nvstream/av/DecodeUnit.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.limelight.nvstream.av; - -public class DecodeUnit { - public static final int DU_FLAG_CODEC_CONFIG = 0x1; - public static final int DU_FLAG_SYNC_FRAME = 0x2; - - private ByteBufferDescriptor bufferHead; - private int dataLength; - private int frameNumber; - private long receiveTimestamp; - private int flags; - - public DecodeUnit() { - } - - public void initialize(ByteBufferDescriptor bufferHead, int dataLength, - int frameNumber, long receiveTimestamp, int flags) - { - this.bufferHead = bufferHead; - this.dataLength = dataLength; - this.frameNumber = frameNumber; - this.receiveTimestamp = receiveTimestamp; - this.flags = flags; - } - - public long getReceiveTimestamp() - { - return receiveTimestamp; - } - - public ByteBufferDescriptor getBufferHead() - { - return bufferHead; - } - - public int getDataLength() - { - return dataLength; - } - - public int getFrameNumber() - { - return frameNumber; - } - - public int getFlags() - { - return flags; - } -} diff --git a/moonlight-common/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java b/moonlight-common/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java index 82e7895d..76c56316 100644 --- a/moonlight-common/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java +++ b/moonlight-common/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java @@ -1,14 +1,11 @@ package com.limelight.nvstream.av.audio; public interface AudioRenderer { - // playDecodedAudio() is lightweight, so don't use an extra thread for playback - public static final int CAPABILITY_DIRECT_SUBMIT = 0x1; + int getCapabilities(); - public int getCapabilities(); + void setup(int audioConfiguration); - public boolean streamInitialized(int channelCount, int channelMask, int samplesPerFrame, int sampleRate); + void playDecodedAudio(byte[] audioData); - public void playDecodedAudio(byte[] audioData, int offset, int length); - - public void streamClosing(); + void cleanup(); } diff --git a/moonlight-common/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java b/moonlight-common/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java index 533d0ad3..7e7bbe76 100644 --- a/moonlight-common/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java +++ b/moonlight-common/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java @@ -1,38 +1,13 @@ package com.limelight.nvstream.av.video; -import com.limelight.nvstream.av.DecodeUnit; - public abstract class VideoDecoderRenderer { - public enum VideoFormat { - Unknown, - H264, - H265 - }; - - public static final int FLAG_PREFER_QUALITY = 0x1; - public static final int FLAG_FORCE_HARDWARE_DECODING = 0x2; - public static final int FLAG_FORCE_SOFTWARE_DECODING = 0x4; - public static final int FLAG_FILL_SCREEN = 0x8; - - // Allows the resolution to dynamically change mid-stream - public static final int CAPABILITY_ADAPTIVE_RESOLUTION = 0x1; - - // Allows decode units to be submitted directly from the receive thread - public static final int CAPABILITY_DIRECT_SUBMIT = 0x2; - - // !!! EXPERIMENTAL !!! - // Allows reference frame invalidation to be use to recover from packet loss - public static final int CAPABILITY_REFERENCE_FRAME_INVALIDATION = 0x4; - public int getCapabilities() { return 0; } - public abstract boolean setup(VideoFormat format, int width, int height, int redrawRate); + public abstract boolean setup(int format, int width, int height, int redrawRate); + + public abstract int submitDecodeUnit(byte[] frameData); - public abstract boolean start(); - - public abstract void stop(); - - public abstract void release(); + public abstract void cleanup(); } diff --git a/moonlight-common/src/main/java/com/limelight/nvstream/jni/MoonBridge.java b/moonlight-common/src/main/java/com/limelight/nvstream/jni/MoonBridge.java new file mode 100644 index 00000000..f0bd81ce --- /dev/null +++ b/moonlight-common/src/main/java/com/limelight/nvstream/jni/MoonBridge.java @@ -0,0 +1,110 @@ +package com.limelight.nvstream.jni; + +import com.limelight.nvstream.NvConnectionListener; +import com.limelight.nvstream.av.audio.AudioRenderer; +import com.limelight.nvstream.av.video.VideoDecoderRenderer; + +public class MoonBridge { + public static final int AUDIO_CONFIGURATION_STEREO = 0; + public static final int AUDIO_CONFIGURATION_51_SURROUND = 1; + + public static final int VIDEO_FORMAT_H264 = 1; + public static final int VIDEO_FORMAT_H265 = 2; + + public static final int CAPABILITY_DIRECT_SUBMIT = 1; + public static final int CAPABILITY_REFERENCE_FRAME_INVALIDATION = 2; + + public static final int DR_OK = 0; + public static final int DR_NEED_IDR = -1; + + private static AudioRenderer audioRenderer; + private static VideoDecoderRenderer videoRenderer; + private static NvConnectionListener connectionListener; + + public static int CAPABILITY_SLICES_PER_FRAME(byte slices) { + return slices << 24; + } + + public static void bridgeDrSetup(int videoFormat, int width, int height, int redrawRate) { + if (videoRenderer != null) { + videoRenderer.setup(videoFormat, width, height, redrawRate); + } + } + + public static void bridgeDrCleanup() { + if (videoRenderer != null) { + videoRenderer.cleanup(); + } + } + + public static int bridgeDrSubmitDecodeUnit(byte[] frameData) { + if (videoRenderer != null) { + return videoRenderer.submitDecodeUnit(frameData); + } + else { + return DR_OK; + } + } + + public static void bridgeArInit(int audioConfiguration) { + if (audioRenderer != null) { + audioRenderer.setup(audioConfiguration); + } + } + + public static void bridgeArCleanup() { + if (audioRenderer != null) { + audioRenderer.cleanup(); + } + } + + public static void bridgeArPlaySample(byte[] pcmData) { + if (audioRenderer != null) { + audioRenderer.playDecodedAudio(pcmData); + } + } + + public static void bridgeClStageStarting(int stage) { + if (connectionListener != null) { + connectionListener.stageStarting(getStageName(stage)); + } + } + + public static void bridgeClStageComplete(int stage) { + if (connectionListener != null) { + connectionListener.stageComplete(getStageName(stage)); + } + } + + public static void bridgeClStageFailed(int stage, long errorCode) { + if (connectionListener != null) { + connectionListener.stageFailed(getStageName(stage), errorCode); + } + } + + public static void bridgeClConnectionStarted() { + if (connectionListener != null) { + connectionListener.connectionStarted(); + } + } + + public static void bridgeClConnectionTerminated(long errorCode) { + if (connectionListener != null) { + connectionListener.connectionTerminated(errorCode); + } + } + + public static void bridgeClDisplayMessage(String message) { + if (connectionListener != null) { + connectionListener.displayMessage(message); + } + } + + public static void bridgeClDisplayTransientMessage(String message) { + if (connectionListener != null) { + connectionListener.displayTransientMessage(message); + } + } + + public static native String getStageName(int stage); +}