mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-10-26 05:50:05 +00:00
Move opus decoding code
This commit is contained in:
1 parent
be584fb203
commit
40508c2ac2
8 files changed
+114
-96
No files matched your search
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user