diff --git a/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationConfigImpl.java b/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationConfigImpl.java index 881afbda537a1b9df8c47d79943bbee50a1956fb..df366dab9306f1d8baebeb826f5a7789c36c80aa 100644 --- a/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationConfigImpl.java +++ b/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationConfigImpl.java @@ -9,6 +9,7 @@ import org.jitsi.service.configuration.*; import org.jitsi.service.libjitsi.*; import org.jitsi.service.neomedia.format.MediaFormat; +import org.jitsi.util.*; import java.util.*; @@ -22,6 +23,13 @@ public class EncodingConfigurationConfigImpl extends EncodingConfigurationImpl { + /** + * The <tt>Logger</tt> used by this <tt>EncodingConfigurationConfigImpl</tt> + * instance for logging output. + */ + private final Logger logger + = Logger.getLogger(EncodingConfigurationConfigImpl.class); + /** * Holds the prefix that will be used to store properties */ diff --git a/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationImpl.java b/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationImpl.java index f3f5ad7aa59a67a9d2fdef5e832b96f4e7f77bfb..627b9972d619cffdb46dadbc58576b12833ffcc6 100644 --- a/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationImpl.java +++ b/src/org/jitsi/impl/neomedia/codec/EncodingConfigurationImpl.java @@ -8,6 +8,7 @@ import org.jitsi.impl.neomedia.format.*; import org.jitsi.service.neomedia.codec.*; +import org.jitsi.util.*; /** * Configuration of encoding priorities. @@ -27,6 +28,13 @@ public class EncodingConfigurationImpl extends EncodingConfiguration */ public static final boolean G729 = false; + /** + * The <tt>Logger</tt> used by this <tt>EncodingConfigurationImpl</tt> + * instance for logging output. + */ + private final Logger logger + = Logger.getLogger(EncodingConfigurationImpl.class); + /** * Constructor. Loads the default preferences. */ diff --git a/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaDecoder.java b/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaDecoder.java index 77147a6700d3f7dd299832111545c3d00746515a..b74f998348e62fc2bb1f4d5226c895a62b3cc3e7 100644 --- a/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaDecoder.java +++ b/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaDecoder.java @@ -10,15 +10,24 @@ import javax.media.format.*; import org.jitsi.impl.neomedia.codec.*; +import org.jitsi.util.*; /** * Implements the SILK decoder as an FMJ/JMF <tt>Codec</tt>. * * @author Dingxin Xu + * @author Boris Grozev */ public class JavaDecoder extends AbstractCodecExt { + /** + * The <tt>Logger</tt> used by this <tt>JavaDecoder</tt> instance + * for logging output. + */ + private final Logger logger + = Logger.getLogger(JavaDecoder.class); + /** * The duration of a frame in milliseconds as defined by the SILK standard. */ @@ -71,6 +80,30 @@ public class JavaDecoder */ private final short[] outputLength = new short[1]; + /** + * Previous packet RTP sequence number + */ + 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; + + /** + * Temporary buffer used to hold the lbrr data when decoding FEC. Defined + * here to avoid using <tt>new</tt> in <tt>doProcess</tt>. + */ + private byte[] lbrrData = new byte[JavaEncoder.MAX_BYTES_PER_FRAME]; + + /** + * Temporary buffer used when decoding FEC. Defined here to + * avoid using <tt>new</tt> in <tt>doProcess</tt>. + */ + private short[] lbrrBytes = new short[1]; + /** * Initializes a new <tt>JavaDecoder</tt> instance. */ @@ -115,38 +148,95 @@ protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) short[] outputData = validateShortArraySize(outputBuffer, frameLength); int outputOffset = 0; + boolean decodeFEC = false; + + /* Check whether a packet has been lost. + * If a packet has more than one frame, we go through each frame in a + * new call to <tt>process</tt>, so having the same sequence number as + * on the previous pass is fine. */ + long sequenceNumber = inputBuffer.getSequenceNumber(); + if(firstPacketProcessed && + sequenceNumber != lastPacketSeq && + sequenceNumber != lastPacketSeq+1 && + /* RTP sequence number is a 16bit field */ + !(lastPacketSeq == 65535 && sequenceNumber == 0)) + decodeFEC = true; + int processed; - outputLength[0] = frameLength; - if (Silk_dec_API.SKP_Silk_SDK_Decode( - decState, decControl, - 0, - inputData, inputOffset, inputLength, - outputData, outputOffset, outputLength) - == 0) + /* Decode packet normally */ + if(!decodeFEC) { - outputBuffer.setDuration(FRAME_DURATION * 1000000); - outputBuffer.setLength(outputLength[0]); - outputBuffer.setOffset(outputOffset); + outputLength[0] = frameLength; + if (Silk_dec_API.SKP_Silk_SDK_Decode( + decState, decControl, + 0, + inputData, inputOffset, inputLength, + outputData, outputOffset, outputLength) + == 0) + { + outputBuffer.setDuration(FRAME_DURATION * 1000000); + outputBuffer.setLength(outputLength[0]); + outputBuffer.setOffset(outputOffset); - if (decControl.moreInternalDecoderFrames == 0) - processed = BUFFER_PROCESSED_OK; + if (decControl.moreInternalDecoderFrames == 0) + processed = BUFFER_PROCESSED_OK; + else + { + framesPerPayload++; + processed + = (framesPerPayload >= MAX_FRAMES_PER_PAYLOAD) + ? BUFFER_PROCESSED_OK + : INPUT_BUFFER_NOT_CONSUMED; + } + } else + processed = BUFFER_PROCESSED_FAILED; + + if ((processed & INPUT_BUFFER_NOT_CONSUMED) + != INPUT_BUFFER_NOT_CONSUMED) + framesPerPayload = 0; + } + else /* Decode the packet's FEC data */ + { + outputLength[0] = frameLength; + + lbrrBytes[0] = 0; + Silk_dec_API.SKP_Silk_SDK_search_for_LBRR( + inputData, inputOffset, (short)inputLength, + 1 /* previous packet */, + lbrrData, 0, lbrrBytes); + + if(logger.isTraceEnabled()) { - framesPerPayload++; - processed - = (framesPerPayload >= MAX_FRAMES_PER_PAYLOAD) - ? BUFFER_PROCESSED_OK - : INPUT_BUFFER_NOT_CONSUMED; + logger.trace("Packet loss detected. Last seen " + lastPacketSeq + + ", current " + sequenceNumber); + logger.trace("Looking for LBRR info, got " + lbrrBytes[0] + "bytes"); } - } - else - processed = BUFFER_PROCESSED_FAILED; - if ((processed & INPUT_BUFFER_NOT_CONSUMED) - != INPUT_BUFFER_NOT_CONSUMED) - framesPerPayload = 0; + if(lbrrBytes[0] == 0) + //No FEC data found, process the normal data in the packet next + processed = INPUT_BUFFER_NOT_CONSUMED; + else if(Silk_dec_API.SKP_Silk_SDK_Decode( + decState, decControl, 0, + lbrrData, 0, lbrrBytes[0], + outputData, outputOffset, outputLength) + == 0) + { + //Found FEC data, decode it + outputBuffer.setDuration(FRAME_DURATION * 1000000); + outputBuffer.setLength(outputLength[0]); + outputBuffer.setOffset(outputOffset); + + //Go on and process the normal data in the packet next + processed = INPUT_BUFFER_NOT_CONSUMED; + } + else + processed = BUFFER_PROCESSED_FAILED; + } + lastPacketSeq = sequenceNumber; + firstPacketProcessed = true; return processed; } 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 5bfd82eb4f2e449d61aaadc390e0bdf3970e145a..e7d4f496f53a2d0926bbbd51bf3bf819f1b0b227 100644 --- a/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaEncoder.java +++ b/src/org/jitsi/impl/neomedia/codec/audio/silk/JavaEncoder.java @@ -10,6 +10,8 @@ import javax.media.format.*; import org.jitsi.impl.neomedia.codec.*; +import org.jitsi.service.configuration.*; +import org.jitsi.service.libjitsi.*; /** * Implements the SILK encoder as an FMJ/JMF <tt>Codec</tt>. @@ -27,9 +29,7 @@ public class JavaEncoder * The maximum number of output payload bytes per input frame. Equals peak * bitrate of 100 kbps. */ - private static final int MAX_BYTES_PER_FRAME = 250; - - private static final int PACKET_LOSS_PERCENTAGE = 0; + static final int MAX_BYTES_PER_FRAME = 250; /** * The list of <tt>Format</tt>s of audio data supported as input by @@ -52,8 +52,6 @@ public class JavaEncoder private static final boolean USE_DTX = false; - private static final boolean USE_IN_BAND_FEC = false; - /** * The duration an output <tt>Buffer</tt> produced by this <tt>Codec</tt> * in nanosecond. @@ -141,16 +139,41 @@ protected void doOpen() double sampleRate = inputFormat.getSampleRate(); int channels = inputFormat.getChannels(); + + ConfigurationService cfg = LibJitsi.getConfigurationService(); + //TODO: we should have a default value dependent on the SDP parameters + //here. + boolean useFEC = cfg.getBoolean("net.java.sip.communicator.impl." + +"neomedia.codec.audio.silk.encoder.usefec", true); + boolean forcePacketLoss = cfg.getBoolean("net.java.sip.communicator." + + "impl.neomedia.codec.audio.silk.encoder." + + "forcepacketloss", true); + + //Update the statically defined value for "speech activity threshold" + //according to our configuration + String satStr = cfg.getString("net.java.sip." + + "communicator.impl.neomedia.codec.audio.silk.encoder.sat", "0.5"); + float sat = Silk_define_FLP.LBRR_SPEECH_ACTIVITY_THRES; + try + { + sat = Float.parseFloat(satStr); + } + catch (Exception e){} + Silk_define_FLP.LBRR_SPEECH_ACTIVITY_THRES = sat; + encControl.API_sampleRate = (int) sampleRate; encControl.bitRate = BITRATE; encControl.complexity = COMPLEXITY; encControl.maxInternalSampleRate = encControl.API_sampleRate; - encControl.packetLossPercentage = PACKET_LOSS_PERCENTAGE; + //At the moment we do not support dynamically setting the expected + //packet loss. Therefore to force the encoder to always expect packet + //loss we set this higher than all thresholds. + encControl.packetLossPercentage = forcePacketLoss ? 100 : 0; encControl.packetSize = (int) ((JavaDecoder.FRAME_DURATION * sampleRate * channels) / 1000); encControl.useDTX = USE_DTX ? 1 : 0; - encControl.useInBandFEC = USE_IN_BAND_FEC ? 1 : 0; + encControl.useInBandFEC = useFEC ? 1 : 0; } protected int doProcess(Buffer inputBuffer, Buffer outputBuffer) diff --git a/src/org/jitsi/impl/neomedia/codec/audio/silk/Silk_define_FLP.java b/src/org/jitsi/impl/neomedia/codec/audio/silk/Silk_define_FLP.java index 34f411e9ff1f4d6ffcb18e238e0c036916b6383d..de5ed003ffc516e76dde59f402e509cd5d0f3a6e 100644 --- a/src/org/jitsi/impl/neomedia/codec/audio/silk/Silk_define_FLP.java +++ b/src/org/jitsi/impl/neomedia/codec/audio/silk/Silk_define_FLP.java @@ -71,7 +71,7 @@ public class Silk_define_FLP static final float SPEECH_ACTIVITY_DTX_THRES = 0.1f; /* Speech Activity LBRR enable threshold (needs tuning) */ - static final float LBRR_SPEECH_ACTIVITY_THRES = 0.5f; + static float LBRR_SPEECH_ACTIVITY_THRES = 0.5f; static final float Q14_CONVERSION_FAC = 6.1035e-005f; // 1 / 2^14 } diff --git a/src/org/jitsi/service/neomedia/codec/EncodingConfiguration.java b/src/org/jitsi/service/neomedia/codec/EncodingConfiguration.java index 936e9a632360e8349d75d18137f65452196d2d6a..f90d3d83efedb2f64c3671aa60a0148034132070 100644 --- a/src/org/jitsi/service/neomedia/codec/EncodingConfiguration.java +++ b/src/org/jitsi/service/neomedia/codec/EncodingConfiguration.java @@ -27,7 +27,7 @@ public class EncodingConfiguration * The <tt>Logger</tt> used by this <tt>EncodingConfiguration</tt> instance * for logging output. */ - protected final Logger logger + private final Logger logger = Logger.getLogger(EncodingConfiguration.class); /**