Skip to content
Snippets Groups Projects
Commit 2d544b60 authored by Boris Grozev's avatar Boris Grozev
Browse files

Enables FEC for Opus.

parent 9b4ebdd7
No related branches found
No related tags found
No related merge requests found
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
import net.sf.fmj.media.*; import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*; import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*; import org.jitsi.service.neomedia.codec.*;
import org.jitsi.service.neomedia.control.*;
import java.awt.*;
/** /**
* Implements an Opus decoder. * Implements an Opus decoder.
...@@ -20,6 +23,7 @@ ...@@ -20,6 +23,7 @@
*/ */
public class JNIDecoder public class JNIDecoder
extends AbstractCodecExt extends AbstractCodecExt
implements FECDecoderControl
{ {
/** /**
* The list of <tt>Format</tt>s of audio data supported as input by * The list of <tt>Format</tt>s of audio data supported as input by
...@@ -49,7 +53,7 @@ public class JNIDecoder ...@@ -49,7 +53,7 @@ public class JNIDecoder
static static
{ {
SUPPORTED_INPUT_FORMATS SUPPORTED_INPUT_FORMATS
= new Format[] {new AudioFormat(Constants.OPUS_RTP)}; = new Format[] {new AudioFormat(Constants.OPUS_RTP)};
} }
/** /**
...@@ -67,6 +71,23 @@ public class JNIDecoder ...@@ -67,6 +71,23 @@ public class JNIDecoder
*/ */
private int outputSamplingRate = 48000; 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. * Initializes a new <tt>JNIDecoder</tt> instance.
*/ */
...@@ -78,6 +99,8 @@ public JNIDecoder() ...@@ -78,6 +99,8 @@ public JNIDecoder()
SUPPORTED_OUTPUT_FORMATS); SUPPORTED_OUTPUT_FORMATS);
inputFormats = SUPPORTED_INPUT_FORMATS; inputFormats = SUPPORTED_INPUT_FORMATS;
addControl(this);
} }
/** /**
...@@ -130,18 +153,28 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) ...@@ -130,18 +153,28 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer)
if (null == setInputFormat(inputFormat)) if (null == setInputFormat(inputFormat))
return BUFFER_PROCESSED_FAILED; 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 inputOffset = inputBuffer.getOffset();
int inputLength = inputBuffer.getLength(); int inputLength = inputBuffer.getLength();
int outputLength = Opus.decoder_get_nb_samples(decoder, int outputLength = Opus.decoder_get_nb_samples(decoder,
input, inputOffset, inputLength) * 2 /* sizeof(short) */; inputData, inputOffset, inputLength) * 2 /* sizeof(short) */;
byte[] output = validateByteArraySize(outputBuffer, outputLength); byte[] outputData = validateByteArraySize(outputBuffer, outputLength);
int samplesCount = Opus.decode(decoder, input, inputOffset, inputLength, int samplesCount = Opus.decode(decoder,
output, outputLength, 0); inputData, inputOffset, inputLength,
outputData, outputLength,
decodeFec ? 1 : 0);
if (samplesCount > 0) if (samplesCount > 0)
{ {
...@@ -150,6 +183,8 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) ...@@ -150,6 +183,8 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer)
outputBuffer.setFormat(getOutputFormat()); outputBuffer.setFormat(getOutputFormat());
outputBuffer.setLength(2*samplesCount); //16bit pcm outputBuffer.setLength(2*samplesCount); //16bit pcm
outputBuffer.setOffset(0); outputBuffer.setOffset(0);
if(decodeFec)
nbDecodedFec++;
} }
else else
{ {
...@@ -157,6 +192,16 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) ...@@ -157,6 +192,16 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer)
discardOutputBuffer(outputBuffer); discardOutputBuffer(outputBuffer);
} }
firstPacketProcessed = true;
if(decodeFec)
{
lastPacketSeq = inputSequenceNumber - 1;
return BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED;
}
lastPacketSeq = inputSequenceNumber;
return BUFFER_PROCESSED_OK; return BUFFER_PROCESSED_OK;
} }
...@@ -245,4 +290,24 @@ public Format setInputFormat(Format format) ...@@ -245,4 +290,24 @@ public Format setInputFormat(Format format)
} }
return inputFormat; 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;
}
} }
...@@ -492,7 +492,8 @@ public void setExpectedPacketLoss(int percentage) ...@@ -492,7 +492,8 @@ public void setExpectedPacketLoss(int percentage)
* *
* @return null * @return null
*/ */
public Component getControlComponent() { public Component getControlComponent()
{
return null; return null;
} }
} }
...@@ -389,7 +389,8 @@ public void setExpectedPacketLoss(int percentage) ...@@ -389,7 +389,8 @@ public void setExpectedPacketLoss(int percentage)
* *
* @return null * @return null
*/ */
public Component getControlComponent() { public Component getControlComponent()
{
return null; return null;
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment