From fbd61d2a21802c110d0a2c755709e613564f0d41 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 17 Oct 2015 17:15:22 -0700 Subject: [PATCH] Transition to the Opus Multistream Decoder API --- .../nvstream/StreamConfiguration.java | 18 ++++++++++++++++++ .../nvstream/av/audio/AudioDepacketizer.java | 11 +++++++---- .../nvstream/av/audio/AudioRenderer.java | 2 +- .../nvstream/av/audio/AudioStream.java | 17 +++++++++++++---- .../nvstream/av/audio/OpusDecoder.java | 5 +---- .../limelight/nvstream/rtsp/SdpGenerator.java | 10 ++++++++++ 6 files changed, 50 insertions(+), 13 deletions(-) diff --git a/moonlight-common/src/com/limelight/nvstream/StreamConfiguration.java b/moonlight-common/src/com/limelight/nvstream/StreamConfiguration.java index dc1db3c8..1f5ca10c 100644 --- a/moonlight-common/src/com/limelight/nvstream/StreamConfiguration.java +++ b/moonlight-common/src/com/limelight/nvstream/StreamConfiguration.java @@ -14,6 +14,8 @@ public class StreamConfiguration { private boolean playLocalAudio; private int maxPacketSize; private boolean remote; + private int audioChannelMask; + private int audioChannelCount; public static class Builder { private StreamConfiguration config = new StreamConfiguration(); @@ -64,6 +66,12 @@ public class StreamConfiguration { return this; } + public StreamConfiguration.Builder setAudioParameters(int audioChannelMask, int audioChannelCount) { + config.audioChannelCount = audioChannelCount; + config.audioChannelMask = audioChannelMask; + return this; + } + public StreamConfiguration build() { return config; } @@ -79,6 +87,8 @@ public class StreamConfiguration { this.maxPacketSize = 1024; this.sops = true; this.enableAdaptiveResolution = false; + this.audioChannelCount = 2; + this.audioChannelMask = 0x3; } public int getWidth() { @@ -120,4 +130,12 @@ public class StreamConfiguration { public boolean getRemote() { return remote; } + + public int getAudioChannelCount() { + return audioChannelCount; + } + + public int getAudioChannelMask() { + return audioChannelMask; + } } diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java index fd550c88..e608076b 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java @@ -12,6 +12,8 @@ public class AudioDepacketizer { private static final int DU_LIMIT = 30; private AbstractPopulatedBufferList decodedUnits; + private final int channelCount; + // Direct submit state private AudioRenderer directSubmitRenderer; private byte[] directSubmitData; @@ -22,16 +24,17 @@ public class AudioDepacketizer { // Sequencing state private short lastSequenceNumber; - public AudioDepacketizer(AudioRenderer directSubmitRenderer) + public AudioDepacketizer(AudioRenderer directSubmitRenderer, final int channelCount, final int bufferSizeShorts) { this.directSubmitRenderer = directSubmitRenderer; + this.channelCount = channelCount; if (directSubmitRenderer != null) { - this.directSubmitData = new byte[OpusDecoder.getMaxOutputShorts()*2]; + this.directSubmitData = new byte[bufferSizeShorts*2]; } else { decodedUnits = new AtomicPopulatedBufferList(DU_LIMIT, new AbstractPopulatedBufferList.BufferFactory() { public Object createFreeBuffer() { - return new ByteBufferDescriptor(new byte[OpusDecoder.getMaxOutputShorts()*2], 0, OpusDecoder.getMaxOutputShorts()*2); + return new ByteBufferDescriptor(new byte[bufferSizeShorts*2], 0, bufferSizeShorts*2); } public void cleanupObject(Object o) { @@ -66,7 +69,7 @@ public class AudioDepacketizer { if (decodeLen > 0) { // Return value of decode is frames (shorts) decoded per channel - decodeLen *= 2*OpusDecoder.getChannelCount(); + decodeLen *= 2*channelCount; if (directSubmitRenderer != null) { directSubmitRenderer.playDecodedAudio(directSubmitData, 0, decodeLen); diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java index a66c3902..82e7895d 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java @@ -6,7 +6,7 @@ public interface AudioRenderer { public int getCapabilities(); - public boolean streamInitialized(int channelCount, int sampleRate); + public boolean streamInitialized(int channelCount, int channelMask, int samplesPerFrame, int sampleRate); public void playDecodedAudio(byte[] audioData, int offset, int length); diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java index 4820a393..1b738106 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java @@ -15,6 +15,9 @@ import com.limelight.nvstream.av.RtpReorderQueue; public class AudioStream { private static final int RTP_PORT = 48000; + private static final int SAMPLE_RATE = 48000; + private static final int SHORTS_PER_CHANNEL = 240; + private static final int RTP_RECV_BUFFER = 64 * 1024; private static final int MAX_PACKET_SIZE = 100; @@ -94,20 +97,26 @@ public class AudioStream { { int err; - err = OpusDecoder.init(); + err = OpusDecoder.init(SAMPLE_RATE, context.streamConfig.getAudioChannelCount(), + 1, 1, new byte[]{0, 1, 2, 3, 4, 5, 6}); if (err != 0) { throw new IllegalStateException("Opus decoder failed to initialize"); } - if (!streamListener.streamInitialized(OpusDecoder.getChannelCount(), OpusDecoder.getSampleRate())) { + if (!streamListener.streamInitialized(context.streamConfig.getAudioChannelCount(), + context.streamConfig.getAudioChannelMask(), + context.streamConfig.getAudioChannelCount()*SHORTS_PER_CHANNEL, + SAMPLE_RATE)) { return false; } if ((streamListener.getCapabilities() & AudioRenderer.CAPABILITY_DIRECT_SUBMIT) != 0) { - depacketizer = new AudioDepacketizer(streamListener); + depacketizer = new AudioDepacketizer(streamListener, context.streamConfig.getAudioChannelCount(), + context.streamConfig.getAudioChannelCount()*SHORTS_PER_CHANNEL); } else { - depacketizer = new AudioDepacketizer(null); + depacketizer = new AudioDepacketizer(null, context.streamConfig.getAudioChannelCount(), + context.streamConfig.getAudioChannelCount()*SHORTS_PER_CHANNEL); } return true; diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java b/moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java index a3d6d740..37212be3 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java @@ -5,10 +5,7 @@ public class OpusDecoder { System.loadLibrary("nv_opus_dec"); } - public static native int init(); + public static native int init(int sampleRate, int channelCount, int streams, int coupledStreams, byte[] mapping); 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, byte[] outpcmdata); } diff --git a/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java b/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java index 72e98f48..5450d1c2 100644 --- a/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java +++ b/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java @@ -57,6 +57,16 @@ public class SdpGenerator { // Use slicing for increased performance on some decoders addSessionAttribute(config, "x-nv-video[0].videoEncoderSlicesPerFrame", "4"); + + // Enable surround sound if configured for it + addSessionAttribute(config, "x-nv-audio.surround.numChannels", ""+context.streamConfig.getAudioChannelCount()); + addSessionAttribute(config, "x-nv-audio.surround.channelMask", ""+context.streamConfig.getAudioChannelMask()); + if (context.streamConfig.getAudioChannelCount() > 2) { + addSessionAttribute(config, "x-nv-audio.surround.enable", "1"); + } + else { + addSessionAttribute(config, "x-nv-audio.surround.enable", "0"); + } } public static String generateSdpFromContext(ConnectionContext context) {