Transition to Opus Multistream Decoder API

This commit is contained in:
Cameron Gutman 2015-10-17 17:16:58 -07:00
parent c025f9f02b
commit c9014da186
4 changed files with 45 additions and 61 deletions

View File

@ -9,14 +9,13 @@ import com.limelight.nvstream.av.audio.AudioRenderer;
public class AndroidAudioRenderer implements AudioRenderer { public class AndroidAudioRenderer implements AudioRenderer {
private static final int FRAME_SIZE = 960;
private AudioTrack track; private AudioTrack track;
@Override @Override
public boolean streamInitialized(int channelCount, int sampleRate) { public boolean streamInitialized(int channelCount, int channelMask, int samplesPerFrame, int sampleRate) {
int channelConfig; int channelConfig;
int bufferSize; int bufferSize;
int bytesPerFrame = (samplesPerFrame * 2);
switch (channelCount) switch (channelCount)
{ {
@ -26,6 +25,12 @@ public class AndroidAudioRenderer implements AudioRenderer {
case 2: case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO; channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break; break;
case 4:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
default: default:
LimeLog.severe("Decoder returned unhandled channel count"); LimeLog.severe("Decoder returned unhandled channel count");
return false; return false;
@ -38,7 +43,7 @@ public class AndroidAudioRenderer implements AudioRenderer {
// use the recommended larger buffer size. // use the recommended larger buffer size.
try { try {
// Buffer two frames of audio if possible // Buffer two frames of audio if possible
bufferSize = FRAME_SIZE * 2; bufferSize = bytesPerFrame * 2;
track = new AudioTrack(AudioManager.STREAM_MUSIC, track = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, sampleRate,
@ -59,10 +64,10 @@ public class AndroidAudioRenderer implements AudioRenderer {
bufferSize = Math.max(AudioTrack.getMinBufferSize(sampleRate, bufferSize = Math.max(AudioTrack.getMinBufferSize(sampleRate,
channelConfig, channelConfig,
AudioFormat.ENCODING_PCM_16BIT), AudioFormat.ENCODING_PCM_16BIT),
FRAME_SIZE * 2); bytesPerFrame * 2);
// Round to next frame // Round to next frame
bufferSize = (((bufferSize + (FRAME_SIZE - 1)) / FRAME_SIZE) * FRAME_SIZE); bufferSize = (((bufferSize + (bytesPerFrame - 1)) / bytesPerFrame) * bytesPerFrame);
track = new AudioTrack(AudioManager.STREAM_MUSIC, track = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, sampleRate,

View File

@ -1,16 +1,20 @@
#include <stdlib.h> #include <stdlib.h>
#include <opus.h> #include <opus_multistream.h>
#include "nv_opus_dec.h" #include "nv_opus_dec.h"
OpusDecoder* decoder; OpusMSDecoder* decoder;
// This function must be called before // This function must be called before
// any other decoding functions // any other decoding functions
int nv_opus_init(void) { int nv_opus_init(int sampleRate, int channelCount, int streams,
int coupledStreams, const unsigned char *mapping) {
int err; int err;
decoder = opus_decoder_create( decoder = opus_multistream_decoder_create(
nv_opus_get_sample_rate(), sampleRate,
nv_opus_get_channel_count(), channelCount,
streams,
coupledStreams,
mapping,
&err); &err);
return err; return err;
} }
@ -19,36 +23,20 @@ int nv_opus_init(void) {
// decoding is finished // decoding is finished
void nv_opus_destroy(void) { void nv_opus_destroy(void) {
if (decoder != NULL) { if (decoder != NULL) {
opus_decoder_destroy(decoder); opus_multistream_decoder_destroy(decoder);
} }
} }
// The Opus stream is stereo
int nv_opus_get_channel_count(void) {
return 2;
}
// This number assumes 16-bit samples at 48 KHz with 2.5 ms frames
int nv_opus_get_max_out_shorts(void) {
return 240*nv_opus_get_channel_count();
}
// The Opus stream is 48 KHz
int nv_opus_get_sample_rate(void) {
return 48000;
}
// outpcmdata must be 5760*2 shorts in length
// packets must be decoded in order // packets must be decoded in order
// a packet loss must call this function with NULL indata and 0 inlen // a packet loss must call this function with NULL indata and 0 inlen
// returns the number of decoded samples // returns the number of decoded samples
int nv_opus_decode(unsigned char* indata, int inlen, short* outpcmdata) { int nv_opus_decode(unsigned char* indata, int inlen, short* outpcmdata, int framesize) {
int err; int err;
// Decoding to 16-bit PCM with FEC off // Decoding to 16-bit PCM with FEC off
// Maximum length assuming 48KHz sample rate // Maximum length assuming 48KHz sample rate
err = opus_decode(decoder, indata, inlen, err = opus_multistream_decode(decoder, indata, inlen,
outpcmdata, 512, 0); outpcmdata, framesize, 0);
return err; return err;
} }

View File

@ -1,6 +1,4 @@
int nv_opus_init(void); int nv_opus_init(int sampleRate, int channelCount, int streams,
int coupledStreams, const unsigned char *mapping);
void nv_opus_destroy(void); void nv_opus_destroy(void);
int nv_opus_get_channel_count(void); int nv_opus_decode(unsigned char* indata, int inlen, short* outpcmdata, int framesize);
int nv_opus_get_max_out_shorts(void);
int nv_opus_get_sample_rate(void);
int nv_opus_decode(unsigned char* indata, int inlen, short* outpcmdata);

View File

@ -6,8 +6,19 @@
// This function must be called before // This function must be called before
// any other decoding functions // any other decoding functions
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_init(JNIEnv *env, jobject this) { Java_com_limelight_nvstream_av_audio_OpusDecoder_init(JNIEnv *env, jobject this,
return nv_opus_init(); int sampleRate, int channelCount, int streams,
int coupledStreams, jbyteArray mapping) {
jbyte* jni_mapping_data;
jint ret;
jni_mapping_data = (*env)->GetByteArrayElements(env, mapping, 0);
ret = nv_opus_init(sampleRate, channelCount, streams, coupledStreams, jni_mapping_data);
(*env)->ReleaseByteArrayElements(env, mapping, jni_mapping_data, JNI_ABORT);
return ret;
} }
// This function must be called after // This function must be called after
@ -17,25 +28,6 @@ Java_com_limelight_nvstream_av_audio_OpusDecoder_destroy(JNIEnv *env, jobject th
nv_opus_destroy(); nv_opus_destroy();
} }
// The Opus stream is stereo
JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_getChannelCount(JNIEnv *env, jobject this) {
return nv_opus_get_channel_count();
}
// This number assumes 2 channels at 48 KHz
JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_getMaxOutputShorts(JNIEnv *env, jobject this) {
return nv_opus_get_max_out_shorts();
}
// The Opus stream is 48 KHz
JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_getSampleRate(JNIEnv *env, jobject this) {
return nv_opus_get_sample_rate();
}
// outpcmdata must be 5760*2 shorts in length
// packets must be decoded in order // packets must be decoded in order
// a packet loss must call this function with NULL indata and 0 inlen // a packet loss must call this function with NULL indata and 0 inlen
// returns the number of decoded samples // returns the number of decoded samples
@ -53,13 +45,14 @@ Java_com_limelight_nvstream_av_audio_OpusDecoder_decode(
if (indata != NULL) { if (indata != NULL) {
jni_input_data = (*env)->GetByteArrayElements(env, indata, 0); jni_input_data = (*env)->GetByteArrayElements(env, indata, 0);
ret = nv_opus_decode(&jni_input_data[inoff], inlen, (jshort*)jni_pcm_data); ret = nv_opus_decode(&jni_input_data[inoff], inlen, (jshort*)jni_pcm_data,
(*env)->GetArrayLength(env, outpcmdata)/2);
// The input data isn't changed so it can be safely aborted // The input data isn't changed so it can be safely aborted
(*env)->ReleaseByteArrayElements(env, indata, jni_input_data, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, indata, jni_input_data, JNI_ABORT);
} }
else { else {
ret = nv_opus_decode(NULL, 0, (jshort*)jni_pcm_data); ret = nv_opus_decode(NULL, 0, (jshort*)jni_pcm_data, (*env)->GetArrayLength(env, outpcmdata)/2);
} }
(*env)->ReleaseByteArrayElements(env, outpcmdata, jni_pcm_data, 0); (*env)->ReleaseByteArrayElements(env, outpcmdata, jni_pcm_data, 0);