Transition to the Opus Multistream Decoder API

This commit is contained in:
Cameron Gutman 2015-10-17 17:15:22 -07:00
parent 469dcab5c7
commit fbd61d2a21
6 changed files with 50 additions and 13 deletions

View File

@ -14,6 +14,8 @@ public class StreamConfiguration {
private boolean playLocalAudio; private boolean playLocalAudio;
private int maxPacketSize; private int maxPacketSize;
private boolean remote; private boolean remote;
private int audioChannelMask;
private int audioChannelCount;
public static class Builder { public static class Builder {
private StreamConfiguration config = new StreamConfiguration(); private StreamConfiguration config = new StreamConfiguration();
@ -64,6 +66,12 @@ public class StreamConfiguration {
return this; return this;
} }
public StreamConfiguration.Builder setAudioParameters(int audioChannelMask, int audioChannelCount) {
config.audioChannelCount = audioChannelCount;
config.audioChannelMask = audioChannelMask;
return this;
}
public StreamConfiguration build() { public StreamConfiguration build() {
return config; return config;
} }
@ -79,6 +87,8 @@ public class StreamConfiguration {
this.maxPacketSize = 1024; this.maxPacketSize = 1024;
this.sops = true; this.sops = true;
this.enableAdaptiveResolution = false; this.enableAdaptiveResolution = false;
this.audioChannelCount = 2;
this.audioChannelMask = 0x3;
} }
public int getWidth() { public int getWidth() {
@ -120,4 +130,12 @@ public class StreamConfiguration {
public boolean getRemote() { public boolean getRemote() {
return remote; return remote;
} }
public int getAudioChannelCount() {
return audioChannelCount;
}
public int getAudioChannelMask() {
return audioChannelMask;
}
} }

View File

@ -12,6 +12,8 @@ public class AudioDepacketizer {
private static final int DU_LIMIT = 30; private static final int DU_LIMIT = 30;
private AbstractPopulatedBufferList<ByteBufferDescriptor> decodedUnits; private AbstractPopulatedBufferList<ByteBufferDescriptor> decodedUnits;
private final int channelCount;
// Direct submit state // Direct submit state
private AudioRenderer directSubmitRenderer; private AudioRenderer directSubmitRenderer;
private byte[] directSubmitData; private byte[] directSubmitData;
@ -22,16 +24,17 @@ public class AudioDepacketizer {
// Sequencing state // Sequencing state
private short lastSequenceNumber; private short lastSequenceNumber;
public AudioDepacketizer(AudioRenderer directSubmitRenderer) public AudioDepacketizer(AudioRenderer directSubmitRenderer, final int channelCount, final int bufferSizeShorts)
{ {
this.directSubmitRenderer = directSubmitRenderer; this.directSubmitRenderer = directSubmitRenderer;
this.channelCount = channelCount;
if (directSubmitRenderer != null) { if (directSubmitRenderer != null) {
this.directSubmitData = new byte[OpusDecoder.getMaxOutputShorts()*2]; this.directSubmitData = new byte[bufferSizeShorts*2];
} }
else { else {
decodedUnits = new AtomicPopulatedBufferList<ByteBufferDescriptor>(DU_LIMIT, new AbstractPopulatedBufferList.BufferFactory() { decodedUnits = new AtomicPopulatedBufferList<ByteBufferDescriptor>(DU_LIMIT, new AbstractPopulatedBufferList.BufferFactory() {
public Object createFreeBuffer() { 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) { public void cleanupObject(Object o) {
@ -66,7 +69,7 @@ public class AudioDepacketizer {
if (decodeLen > 0) { if (decodeLen > 0) {
// Return value of decode is frames (shorts) decoded per channel // Return value of decode is frames (shorts) decoded per channel
decodeLen *= 2*OpusDecoder.getChannelCount(); decodeLen *= 2*channelCount;
if (directSubmitRenderer != null) { if (directSubmitRenderer != null) {
directSubmitRenderer.playDecodedAudio(directSubmitData, 0, decodeLen); directSubmitRenderer.playDecodedAudio(directSubmitData, 0, decodeLen);

View File

@ -6,7 +6,7 @@ public interface AudioRenderer {
public int getCapabilities(); 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); public void playDecodedAudio(byte[] audioData, int offset, int length);

View File

@ -15,6 +15,9 @@ import com.limelight.nvstream.av.RtpReorderQueue;
public class AudioStream { public class AudioStream {
private static final int RTP_PORT = 48000; 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 RTP_RECV_BUFFER = 64 * 1024;
private static final int MAX_PACKET_SIZE = 100; private static final int MAX_PACKET_SIZE = 100;
@ -94,20 +97,26 @@ public class AudioStream {
{ {
int err; 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) { if (err != 0) {
throw new IllegalStateException("Opus decoder failed to initialize"); 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; return false;
} }
if ((streamListener.getCapabilities() & AudioRenderer.CAPABILITY_DIRECT_SUBMIT) != 0) { 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 { else {
depacketizer = new AudioDepacketizer(null); depacketizer = new AudioDepacketizer(null, context.streamConfig.getAudioChannelCount(),
context.streamConfig.getAudioChannelCount()*SHORTS_PER_CHANNEL);
} }
return true; return true;

View File

@ -5,10 +5,7 @@ public class OpusDecoder {
System.loadLibrary("nv_opus_dec"); 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 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); public static native int decode(byte[] indata, int inoff, int inlen, byte[] outpcmdata);
} }

View File

@ -57,6 +57,16 @@ public class SdpGenerator {
// Use slicing for increased performance on some decoders // Use slicing for increased performance on some decoders
addSessionAttribute(config, "x-nv-video[0].videoEncoderSlicesPerFrame", "4"); 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) { public static String generateSdpFromContext(ConnectionContext context) {