From 238fb19f7e198844bbc3370aa6a32d83451cb8d0 Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Mon, 10 May 2010 12:48:30 +0000 Subject: [PATCH] Fix speex sending packets and ticking rtp time, reported by Marian Kechlibar. --- .../neomedia/codec/EncodingConfiguration.java | 9 +- .../codec/audio/speex/JavaEncoder.java | 132 +++++++++++++++--- 2 files changed, 118 insertions(+), 23 deletions(-) diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java b/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java index cb9d9a513..0028f8b9d 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java @@ -161,12 +161,9 @@ public void initializeFormatPreferences() setEncodingPreference("PCMA", 8000, 600); setEncodingPreference("iLBC", 8000, 500); setEncodingPreference("GSM", 8000, 450); - // not selected for use till confirmed its working - // cause uses some old speex impl which seems - // incompatable with some clients - setEncodingPreference("speex", 8000, 0); - setEncodingPreference("speex", 16000, 0); - setEncodingPreference("speex", 32000, 0); + setEncodingPreference("speex", 8000, 352); + setEncodingPreference("speex", 16000, 351); + setEncodingPreference("speex", 32000, 350); setEncodingPreference("DVI4", 8000, 300); setEncodingPreference("DVI4", 16000, 250); setEncodingPreference("G723", 8000, 150); diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/audio/speex/JavaEncoder.java b/src/net/java/sip/communicator/impl/neomedia/codec/audio/speex/JavaEncoder.java index dc2a6e0d7..7a3bf1e25 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/audio/speex/JavaEncoder.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/audio/speex/JavaEncoder.java @@ -11,6 +11,7 @@ import org.xiph.speex.*; import net.java.sip.communicator.impl.neomedia.codec.*; +import net.sf.fmj.media.*; /** * The Speex Encoder @@ -18,16 +19,48 @@ * @author Damian Minkov */ public class JavaEncoder - extends com.ibm.media.codec.audio.AudioCodec + extends AbstractCodec { + /** + * Default output formats. + */ + protected Format[] DEFAULT_OUTPUT_FORMATS = new Format[] { + new AudioFormat(Constants.SPEEX_RTP)}; + + /** + * The last input format used by this instance of the encoder. + * If null we must create the encoder with the current input format. + */ private Format lastFormat = null; + /** + * Our custom output format which computes the rtp data duration correctly. + */ + private AudioFormat outFormat = null; + + /** + * The frame size used for current encoder configuration. + */ private int FRAME_SIZE = -1; + /** + * The speex encoder instance. + */ private SpeexEncoder encoder = null; + /** + * Narrow band used for 8 kHz. + */ final static int NARROW_BAND= 0; + + /** + * Wide band used for 16 kHz. + */ final static int WIDE_BAND= 1; + + /** + * Utra wide band used for 32 kHz. + */ final static int ULTRA_WIDE_BAND= 2; /** @@ -35,7 +68,7 @@ public class JavaEncoder */ public JavaEncoder() { - supportedInputFormats = new AudioFormat[] + this.inputFormats = new AudioFormat[] { new AudioFormat( AudioFormat.LINEAR, @@ -62,11 +95,35 @@ public JavaEncoder() AudioFormat.SIGNED //isSigned()); ) }; + } + + /** + * Returns the name of this plugin/codec. + * @return the name. + */ + public String getName() + { + return "pcm to speex converter"; + } + + /** + * Return the list of formats supported at the output. + * + * @param in the input format. + * @return array of formats supported at output + */ + public Format[] getSupportedOutputFormats(Format in) + { + // null input format + if (in == null) + return DEFAULT_OUTPUT_FORMATS; - defaultOutputFormats = new AudioFormat[] - {new AudioFormat(Constants.SPEEX_RTP)}; + // mismatch input format + if (!(in instanceof AudioFormat) + || (null == AbstractCodecExt.matches(in, inputFormats))) + return new Format[0]; - PLUGIN_NAME = "pcm to speex converter"; + return getMatchingOutputFormats(in); } /** @@ -74,11 +131,11 @@ public JavaEncoder() * @param in input format * @return array of formats that match input ones */ - protected Format[] getMatchingOutputFormats(Format in) + private Format[] getMatchingOutputFormats(Format in) { AudioFormat af = (AudioFormat) in; - supportedOutputFormats = new AudioFormat[] + return new AudioFormat[] {new AudioFormat( Constants.SPEEX_RTP, af.getSampleRate(), @@ -87,8 +144,6 @@ protected Format[] getMatchingOutputFormats(Format in) af.getEndian(), af.getSigned() )}; - - return supportedOutputFormats; } /** @@ -107,30 +162,54 @@ public void close() } + /** + * Initialize the encoder with the supplied input format. + * @param inFormat the input format. + */ private void initConverter(AudioFormat inFormat) { lastFormat = inFormat; + AudioFormat oFormat = (AudioFormat)outputFormat; + outFormat = new AudioFormat( + oFormat.getEncoding(), + oFormat.getSampleRate(), + oFormat.getSampleSizeInBits(), + oFormat.getChannels(), + oFormat.getEndian(), + oFormat.getSigned(), + oFormat.getFrameSizeInBits(), + oFormat.getFrameRate(), + oFormat.getDataType()) + { + /** + * The length in nanoseconds. + */ + public long computeDuration(long l) + { + // 20ms in nano + return 20 * 1000000; + } + }; + encoder = new SpeexEncoder(); int sampleRate = (int)inFormat.getSampleRate(); - + int band = NARROW_BAND; - FRAME_SIZE = 320; if(sampleRate == 16000) { band = WIDE_BAND; - FRAME_SIZE = 640; } else if(sampleRate == 32000) { - band = ULTRA_WIDE_BAND; - FRAME_SIZE = 1280; + band = ULTRA_WIDE_BAND; } encoder.init(band, 4, sampleRate, 1); + FRAME_SIZE = 2 * encoder.getFrameSize(); } /** @@ -175,12 +254,31 @@ public int process(Buffer inputBuffer, Buffer outputBuffer) byte[] buff = new byte[encoder.getProcessedDataByteSize()]; encoder.getProcessedData(buff, 0); - byte[] outData = validateByteArraySize(outputBuffer, buff.length); + /* + * Do not always allocate a new data array for outBuffer, try to reuse + * the existing one if it is suitable. + */ + Object outData = outputBuffer.getData(); + byte[] out; + + if (outData instanceof byte[]) + { + out = (byte[]) outData; + if (out.length < buff.length) + out = null; + } + else + out = null; + if (out == null) + out = new byte[buff.length]; - System.arraycopy(buff, 0, outData, outputBuffer.getOffset(), + System.arraycopy(buff, 0, out, outputBuffer.getOffset(), buff.length); - updateOutput(outputBuffer, outputFormat, outData.length, 0); + outputBuffer.setData(out); + outputBuffer.setLength(out.length); + outputBuffer.setOffset(0); + outputBuffer.setFormat(outFormat); if ( (inpLength - inOffset) > FRAME_SIZE) {