Conceal a lost voice packet with 20 ms, not 120 ms

A lost packet was concealed by decoding "nothing" into a buffer sized
for the longest Opus frame, and Opus fills whatever it is given: every
lost packet played 120 ms of made-up audio in place of 20 ms, piling up
delay on that speaker's line.

Decoders now conceal through their own call that takes the length to
make up, and the stream asks for the length of the last packet it got.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 08:18:06 +00:00
parent cc0243a7c6
commit 75d9606231
5 changed files with 44 additions and 24 deletions

View File

@@ -43,29 +43,27 @@ final class NativeOpusDecoder implements OpusDecoder {
}
/**
* Decodes an Opus packet to interleaved float PCM.
*
* @param packet the encoded packet, or {@code null} to request packet-loss
* concealment (PLC) for a missing frame
* @param packet an Opus packet, at most {@value #MAX_PACKET_BYTES} bytes
* @param out output buffer, at least {@code frameSize * channels} long
* @return number of samples decoded per channel
*/
@Override
public int decode(byte[] packet, float[] out) {
if (closed) throw new IllegalStateException("decoder closed");
MemorySegment data = MemorySegment.NULL;
int len = 0;
if (packet != null) {
if (packet.length > MAX_PACKET_BYTES) {
throw new IllegalArgumentException("packet too large: " + packet.length);
}
MemorySegment.copy(packet, 0, packetBuffer, ValueLayout.JAVA_BYTE, 0, packet.length);
data = packetBuffer;
len = packet.length;
if (packet.length > MAX_PACKET_BYTES) {
throw new IllegalArgumentException("packet too large: " + packet.length);
}
MemorySegment.copy(packet, 0, packetBuffer, ValueLayout.JAVA_BYTE, 0, packet.length);
return decode(packetBuffer, packet.length, out, frameSize);
}
int samples = Opus.decodeFloat(handle, data, len, pcmBuffer, frameSize, 0);
@Override
public int conceal(float[] out, int frameSize) {
return decode(MemorySegment.NULL, 0, out, Math.min(frameSize, this.frameSize));
}
private int decode(MemorySegment data, int len, float[] out, int maxSamples) {
if (closed) throw new IllegalStateException("decoder closed");
int samples = Opus.decodeFloat(handle, data, len, pcmBuffer, maxSamples, 0);
if (samples < 0) {
throw new IllegalStateException("opus_decode_float failed: " + Opus.strerror(samples));
}