diff --git a/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIDecoder.java b/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIDecoder.java index 40d1a50beea325be1d329180517457c4dbadef47..6b5a3e79daecefd8a5898d593e661ccf932012da 100644 --- a/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIDecoder.java +++ b/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIDecoder.java @@ -12,6 +12,9 @@ import net.sf.fmj.media.*; import org.jitsi.impl.neomedia.codec.*; import org.jitsi.service.neomedia.codec.*; +import org.jitsi.service.neomedia.control.*; + +import java.awt.*; /** * Implements an Opus decoder. @@ -20,6 +23,7 @@ */ public class JNIDecoder extends AbstractCodecExt + implements FECDecoderControl { /** * The list of <tt>Format</tt>s of audio data supported as input by @@ -49,7 +53,7 @@ public class JNIDecoder static { SUPPORTED_INPUT_FORMATS - = new Format[] {new AudioFormat(Constants.OPUS_RTP)}; + = new Format[] {new AudioFormat(Constants.OPUS_RTP)}; } /** @@ -67,6 +71,23 @@ public class JNIDecoder */ private int outputSamplingRate = 48000; + /** + * Sequence number of the last packet processed + */ + private long lastPacketSeq; + + /** + * Whether at least one packet has already been processed. Use this to + * prevent FEC data from trying to be decoded from the first packet in a + * session. + */ + private boolean firstPacketProcessed = false; + + /** + * Number of packets decoded with FEC + */ + private int nbDecodedFec = 0; + /** * Initializes a new <tt>JNIDecoder</tt> instance. */ @@ -78,6 +99,8 @@ public JNIDecoder() SUPPORTED_OUTPUT_FORMATS); inputFormats = SUPPORTED_INPUT_FORMATS; + + addControl(this); } /** @@ -130,18 +153,28 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) if (null == setInputFormat(inputFormat)) return BUFFER_PROCESSED_FAILED; } - inputFormat = this.inputFormat; - byte[] input = (byte[]) inputBuffer.getData(); + boolean decodeFec = false; + long inputSequenceNumber = inputBuffer.getSequenceNumber(); + + /* Detect a missing packet, take care of wraps at 2^16 */ + if(firstPacketProcessed && + (inputSequenceNumber != lastPacketSeq + 1) && + !(inputSequenceNumber == 0 && lastPacketSeq == 65535)) + decodeFec = true; + + byte[] inputData = (byte[]) inputBuffer.getData(); int inputOffset = inputBuffer.getOffset(); int inputLength = inputBuffer.getLength(); int outputLength = Opus.decoder_get_nb_samples(decoder, - input, inputOffset, inputLength) * 2 /* sizeof(short) */; - byte[] output = validateByteArraySize(outputBuffer, outputLength); + inputData, inputOffset, inputLength) * 2 /* sizeof(short) */; + byte[] outputData = validateByteArraySize(outputBuffer, outputLength); - int samplesCount = Opus.decode(decoder, input, inputOffset, inputLength, - output, outputLength, 0); + int samplesCount = Opus.decode(decoder, + inputData, inputOffset, inputLength, + outputData, outputLength, + decodeFec ? 1 : 0); if (samplesCount > 0) { @@ -150,6 +183,8 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) outputBuffer.setFormat(getOutputFormat()); outputBuffer.setLength(2*samplesCount); //16bit pcm outputBuffer.setOffset(0); + if(decodeFec) + nbDecodedFec++; } else { @@ -157,6 +192,16 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) discardOutputBuffer(outputBuffer); } + + firstPacketProcessed = true; + + if(decodeFec) + { + lastPacketSeq = inputSequenceNumber - 1; + return BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED; + } + + lastPacketSeq = inputSequenceNumber; return BUFFER_PROCESSED_OK; } @@ -245,4 +290,24 @@ public Format setInputFormat(Format format) } return inputFormat; } + + /** + * Returns the number of packets decoded with FEC + * @return + */ + public int fecPacketsDecoded() + { + return nbDecodedFec; + } + + /** + * Stub. Only added in order to implement the <tt>FECDecoderControl</tt> + * interface. + * + * @return null + */ + public Component getControlComponent() + { + return null; + } } diff --git a/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIEncoder.java b/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIEncoder.java index 013c9988dc204495d74ce53de5abaa0bea1844b0..25c2d1da288e8e62da081b9c17f2a12bc3b81632 100644 --- a/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIEncoder.java +++ b/src/org/jitsi/impl/neomedia/codec/audio/opus/JNIEncoder.java @@ -492,7 +492,8 @@ public void setExpectedPacketLoss(int percentage) * * @return null */ - public Component getControlComponent() { + public Component getControlComponent() + { return null; } } diff --git a/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaEncoder.java b/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaEncoder.java index 85073444e9e41b220ec3931c635b18b1cc9f11cf..e38d030820ebfeff93addb7045baad1d3c06aa14 100644 --- a/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaEncoder.java +++ b/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaEncoder.java @@ -389,7 +389,8 @@ public void setExpectedPacketLoss(int percentage) * * @return null */ - public Component getControlComponent() { + public Component getControlComponent() + { return null; } }