mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-21 03:52:48 +00:00
Transition to the Opus Multistream Decoder API
This commit is contained in:
parent
469dcab5c7
commit
fbd61d2a21
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user