mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-04-03 14:36:09 +00:00
Move audio buffering responsibility onto us and simply fill whatever the runtime gives us
This commit is contained in:
@@ -1,30 +1,36 @@
|
||||
package com.limelight.binding.audio;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
import com.limelight.nvstream.av.ShortBufferDescriptor;
|
||||
import com.limelight.nvstream.av.audio.AudioRenderer;
|
||||
import com.limelight.nvstream.av.audio.OpusDecoder;
|
||||
|
||||
public class JavaxAudioRenderer implements AudioRenderer {
|
||||
|
||||
private SourceDataLine soundLine;
|
||||
private SoundBuffer soundBuffer;
|
||||
private byte[] lineBuffer;
|
||||
|
||||
public static final int STAGING_BUFFERS = 3; // 3 complete frames of audio
|
||||
|
||||
@Override
|
||||
public void playDecodedAudio(short[] pcmData, int offset, int length) {
|
||||
if (soundLine != null) {
|
||||
byte[] pcmDataBytes = new byte[length * 2];
|
||||
ByteBuffer.wrap(pcmDataBytes).asShortBuffer().put(pcmData, offset, length);
|
||||
if (soundLine.available() < length) {
|
||||
soundLine.write(pcmDataBytes, 0, soundLine.available());
|
||||
}
|
||||
else {
|
||||
soundLine.write(pcmDataBytes, 0, pcmDataBytes.length);
|
||||
// Queue the decoded samples into the staging sound buffer
|
||||
soundBuffer.queue(new ShortBufferDescriptor(pcmData, offset, length));
|
||||
|
||||
// If there's space available in the sound line, pull some data out
|
||||
// of the staging buffer and write it to the sound line
|
||||
int available = soundLine.available();
|
||||
if (available > 0) {
|
||||
int written = soundBuffer.fill(lineBuffer, 0, available);
|
||||
if (written > 0) {
|
||||
soundLine.write(lineBuffer, 0, written);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,11 +45,14 @@ public class JavaxAudioRenderer implements AudioRenderer {
|
||||
@Override
|
||||
public void streamInitialized(int channelCount, int sampleRate) {
|
||||
AudioFormat audioFormat = new AudioFormat(sampleRate, 16, channelCount, true, true);
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, OpusDecoder.getMaxOutputShorts());
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
|
||||
try {
|
||||
soundLine = (SourceDataLine) AudioSystem.getLine(info);
|
||||
soundLine.open(audioFormat, OpusDecoder.getMaxOutputShorts()*4*2);
|
||||
soundLine.open(audioFormat);
|
||||
soundLine.start();
|
||||
|
||||
lineBuffer = new byte[soundLine.getBufferSize()];
|
||||
soundBuffer = new SoundBuffer(STAGING_BUFFERS);
|
||||
} catch (LineUnavailableException e) {
|
||||
soundLine = null;
|
||||
}
|
||||
|
||||
53
src/com/limelight/binding/audio/SoundBuffer.java
Normal file
53
src/com/limelight/binding/audio/SoundBuffer.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.limelight.binding.audio;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import com.limelight.nvstream.av.ShortBufferDescriptor;
|
||||
|
||||
public class SoundBuffer {
|
||||
|
||||
private LinkedList<ShortBufferDescriptor> bufferList;
|
||||
private int maxBuffers;
|
||||
|
||||
public SoundBuffer(int maxBuffers) {
|
||||
this.bufferList = new LinkedList<ShortBufferDescriptor>();
|
||||
this.maxBuffers = maxBuffers;
|
||||
}
|
||||
|
||||
public void queue(ShortBufferDescriptor buff) {
|
||||
if (bufferList.size() > maxBuffers) {
|
||||
bufferList.removeFirst();
|
||||
}
|
||||
|
||||
bufferList.addLast(buff);
|
||||
}
|
||||
|
||||
public int fill(byte[] data, int offset, int length) {
|
||||
int filled = 0;
|
||||
|
||||
// Convert offset and length to be relative to shorts
|
||||
offset /= 2;
|
||||
length /= 2;
|
||||
|
||||
ShortBuffer sb = ByteBuffer.wrap(data).asShortBuffer();
|
||||
sb.position(offset);
|
||||
while (length > 0 && !bufferList.isEmpty()) {
|
||||
ShortBufferDescriptor buff = bufferList.getFirst();
|
||||
|
||||
if (buff.length > length) {
|
||||
break;
|
||||
}
|
||||
|
||||
sb.put(buff.data, buff.offset, buff.length);
|
||||
length -= buff.length;
|
||||
filled += buff.length;
|
||||
|
||||
bufferList.removeFirst();
|
||||
}
|
||||
|
||||
// Return bytes instead of shorts
|
||||
return filled * 2;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user