Move opus decoding code

This commit is contained in:
Iwan Timmer committed 2015-03-09 12:16:57 +01:00
1 parent be584fb203
commit 40508c2ac2
8 files changed
+114 -96

No files matched your search

+6 -6
View File
@@ -8,32 +8,32 @@
// 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_binding_audio_OpusDecoder_init(JNIEnv *env, jobject this) {
return nv_opus_init(); return nv_opus_init();
} }
// This function must be called after // This function must be called after
// decoding is finished // decoding is finished
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_destroy(JNIEnv *env, jobject this) { Java_com_limelight_binding_audio_OpusDecoder_destroy(JNIEnv *env, jobject this) {
nv_opus_destroy(); nv_opus_destroy();
} }
// The Opus stream is stereo // The Opus stream is stereo
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_getChannelCount(JNIEnv *env, jobject this) { Java_com_limelight_binding_audio_OpusDecoder_getChannelCount(JNIEnv *env, jobject this) {
return nv_opus_get_channel_count(); return nv_opus_get_channel_count();
} }
// This number assumes 2 channels at 48 KHz // This number assumes 2 channels at 48 KHz
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_getMaxOutputShorts(JNIEnv *env, jobject this) { Java_com_limelight_binding_audio_OpusDecoder_getMaxOutputShorts(JNIEnv *env, jobject this) {
return nv_opus_get_max_out_shorts(); return nv_opus_get_max_out_shorts();
} }
// The Opus stream is 48 KHz // The Opus stream is 48 KHz
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_getSampleRate(JNIEnv *env, jobject this) { Java_com_limelight_binding_audio_OpusDecoder_getSampleRate(JNIEnv *env, jobject this) {
return nv_opus_get_sample_rate(); return nv_opus_get_sample_rate();
} }
@@ -42,7 +42,7 @@ Java_com_limelight_nvstream_av_audio_OpusDecoder_getSampleRate(JNIEnv *env, jobj
// 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
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_av_audio_OpusDecoder_decode( Java_com_limelight_binding_audio_OpusDecoder_decode(
JNIEnv *env, jobject this, // JNI parameters JNIEnv *env, jobject this, // JNI parameters
jbyteArray indata, jint inoff, jint inlen, // Input parameters jbyteArray indata, jint inoff, jint inlen, // Input parameters
jbyteArray outpcmdata) // Output parameter jbyteArray outpcmdata) // Output parameter
+2 -2
View File
@@ -1,7 +1,7 @@
package com.limelight; package com.limelight;
import com.limelight.binding.PlatformBinding; import com.limelight.binding.PlatformBinding;
import com.limelight.binding.audio.FakeAudioRenderer; import com.limelight.binding.audio.FakeAudioDecoderRenderer;
import com.limelight.binding.video.FakeVideoRenderer; import com.limelight.binding.video.FakeVideoRenderer;
import com.limelight.input.EvdevLoader; import com.limelight.input.EvdevLoader;
import com.limelight.input.GamepadMapper; import com.limelight.input.GamepadMapper;
@@ -115,7 +115,7 @@ public class Limelight implements NvConnectionListener {
conn = new NvConnection(host.getHostAddress(), getUniqueId(), this, streamConfig, PlatformBinding.getCryptoProvider()); conn = new NvConnection(host.getHostAddress(), getUniqueId(), this, streamConfig, PlatformBinding.getCryptoProvider());
conn.start(PlatformBinding.getDeviceName(), null, conn.start(PlatformBinding.getDeviceName(), null,
VideoDecoderRenderer.FLAG_PREFER_QUALITY, VideoDecoderRenderer.FLAG_PREFER_QUALITY,
new FakeAudioRenderer(), new FakeAudioDecoderRenderer(),
new FakeVideoRenderer(videoFile)); new FakeVideoRenderer(videoFile));
} }
@@ -5,14 +5,14 @@ import java.net.UnknownHostException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import com.limelight.binding.audio.AlsaAudioRenderer; import com.limelight.binding.audio.AlsaAudioDecoderRenderer;
import com.limelight.binding.video.ImxDecoder; import com.limelight.binding.video.ImxDecoder;
import com.limelight.binding.video.ImxDecoderRenderer; import com.limelight.binding.video.ImxDecoderRenderer;
import com.limelight.binding.crypto.PcCryptoProvider; import com.limelight.binding.crypto.PcCryptoProvider;
import com.limelight.binding.video.OmxDecoder; import com.limelight.binding.video.OmxDecoder;
import com.limelight.binding.video.OmxDecoderRenderer; import com.limelight.binding.video.OmxDecoderRenderer;
import com.limelight.binding.video.AbstractVideoRenderer; import com.limelight.binding.video.AbstractVideoRenderer;
import com.limelight.nvstream.av.audio.AudioRenderer; import com.limelight.nvstream.av.audio.AudioDecoderRenderer;
import com.limelight.nvstream.av.video.VideoDecoderRenderer; import com.limelight.nvstream.av.video.VideoDecoderRenderer;
import com.limelight.nvstream.http.LimelightCryptoProvider; import com.limelight.nvstream.http.LimelightCryptoProvider;
import com.limelight.LimeLog; import com.limelight.LimeLog;
@@ -55,7 +55,7 @@ public class PlatformBinding {
* Gets an instance of an audio decoder/renderer. * Gets an instance of an audio decoder/renderer.
* @return an audio decoder and renderer * @return an audio decoder and renderer
*/ */
public static AudioRenderer getAudioRenderer(String device) { public static AudioDecoderRenderer getAudioRenderer(String device) {
//Try to load local libopus //Try to load local libopus
try { try {
Runtime.getRuntime().load(new File(".").getCanonicalPath()+File.separator+"libopus.so"); Runtime.getRuntime().load(new File(".").getCanonicalPath()+File.separator+"libopus.so");
@@ -66,7 +66,7 @@ public class PlatformBinding {
//Use system opus library //Use system opus library
} }
return new AlsaAudioRenderer(device); return new AlsaAudioDecoderRenderer(device);
} }
/** /**
@@ -0,0 +1,53 @@
package com.limelight.binding.audio;
import com.limelight.LimeLog;
import com.limelight.nvstream.av.audio.AudioDecoderRenderer;
/**
* Audio renderer implementation
* @author Iwan Timmer
*/
public class AlsaAudioDecoderRenderer implements AudioDecoderRenderer {
private String device;
private byte[] decodedData;
public AlsaAudioDecoderRenderer(String device) {
this.device = device;
this.decodedData = new byte[OpusDecoder.getMaxOutputShorts()*2];
int err;
err = OpusDecoder.init();
if (err != 0) {
throw new IllegalStateException("Opus decoder failed to initialize");
}
}
@Override
public boolean streamInitialize() {
return AlsaAudio.init(OpusDecoder.getChannelCount(), OpusDecoder.getSampleRate(), device) == 0;
}
@Override
public void playAudio(byte[] bytes, int offset, int length) {
int decodeLen = OpusDecoder.decode(bytes, offset, length, decodedData);
if (decodeLen > 0) {
//Value of decode is frames (shorts) decoded per channel
decodeLen *= 2*OpusDecoder.getChannelCount();
int rc = AlsaAudio.play(decodedData, 0, decodeLen);
if (rc<0)
LimeLog.warning("Alsa error from writei: "+rc);
else if (rc!=length/4)
LimeLog.warning("Alsa short write, write "+rc+" frames");
}
}
@Override
public void streamClosing() {
AlsaAudio.close();
}
}
@@ -1,43 +0,0 @@
package com.limelight.binding.audio;
import com.limelight.LimeLog;
import com.limelight.nvstream.av.audio.AudioRenderer;
/**
* Audio renderer implementation
* @author Iwan Timmer
*/
public class AlsaAudioRenderer implements AudioRenderer {
private String device;
public AlsaAudioRenderer(String device) {
this.device = device;
}
@Override
public boolean streamInitialized(int channelCount, int sampleRate) {
return AlsaAudio.init(channelCount, sampleRate, device) == 0;
}
@Override
public void playDecodedAudio(byte[] bytes, int offset, int length) {
int rc = AlsaAudio.play(bytes, offset, length);
if (rc<0)
LimeLog.warning("Alsa error from writei: "+rc);
else if (rc!=length/4)
LimeLog.warning("Alsa short write, write "+rc+" frames");
}
@Override
public void streamClosing() {
AlsaAudio.close();
}
@Override
public int getCapabilities() {
return 0;
}
}
@@ -0,0 +1,35 @@
package com.limelight.binding.audio;
import com.limelight.nvstream.av.audio.AudioDecoderRenderer;
/**
* Fake implementation for audio renderer.
* @author Iwan Timmer
*/
public class FakeAudioDecoderRenderer implements AudioDecoderRenderer {
private int dataSize;
private long last;
@Override
public boolean streamInitialize() {
System.out.println("Fake audio output");
return true;
}
@Override
public void playAudio(byte[] audioData, int offset, int length) {
if (System.currentTimeMillis()>last+2000) {
int bitrate = (dataSize/2)/1024;
System.out.println("Audio " + bitrate + "kB/s");
dataSize = 0;
last = System.currentTimeMillis();
}
dataSize += length;
}
@Override
public void streamClosing() {
}
}
@@ -1,41 +0,0 @@
package com.limelight.binding.audio;
import com.limelight.nvstream.av.audio.AudioRenderer;
/**
* Fake implementation for audio renderer.
* @author Iwan Timmer
*/
public class FakeAudioRenderer implements AudioRenderer {
private int dataSize;
private long last;
@Override
public boolean streamInitialized(int channelCount, int sampleRate) {
System.out.println("Fake " + channelCount + " channel " + sampleRate + " samplerate audio output");
last = System.currentTimeMillis();
return true;
}
@Override
public void playDecodedAudio(byte[] audioData, int offset, int length) {
if (System.currentTimeMillis()>last+2000) {
int bitrate = (dataSize/2)/1024;
System.out.println("Audio " + bitrate + "kB/s");
dataSize = 0;
last = System.currentTimeMillis();
}
dataSize += length;
}
@Override
public void streamClosing() {
}
@Override
public int getCapabilities() {
return CAPABILITY_DIRECT_SUBMIT;
}
}
@@ -0,0 +1,14 @@
package com.limelight.binding.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, byte[] outpcmdata);
}