|
|
|
|
@ -12,15 +12,30 @@
|
|
|
|
|
|
|
|
|
|
import org.xiph.speex.*;
|
|
|
|
|
import net.java.sip.communicator.impl.neomedia.codec.*;
|
|
|
|
|
import net.sf.fmj.media.*;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Speex to PCM java decoder
|
|
|
|
|
* @author Damian Minkov
|
|
|
|
|
**/
|
|
|
|
|
public class JavaDecoder
|
|
|
|
|
extends com.ibm.media.codec.audio.AudioCodec
|
|
|
|
|
extends AbstractCodec
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Default output formats.
|
|
|
|
|
*/
|
|
|
|
|
protected Format[] DEFAULT_OUTPUT_FORMATS = new Format[] {
|
|
|
|
|
new AudioFormat(AudioFormat.LINEAR)};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The input format used to initialize the decoder.
|
|
|
|
|
* If null decoder must be created.
|
|
|
|
|
*/
|
|
|
|
|
private Format lastFormat = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The decoder.
|
|
|
|
|
*/
|
|
|
|
|
private SpeexDecoder decoder = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -28,7 +43,7 @@ public class JavaDecoder
|
|
|
|
|
*/
|
|
|
|
|
public JavaDecoder()
|
|
|
|
|
{
|
|
|
|
|
supportedInputFormats = new AudioFormat[]
|
|
|
|
|
this.inputFormats = new AudioFormat[]
|
|
|
|
|
{
|
|
|
|
|
new AudioFormat(Constants.SPEEX_RTP,
|
|
|
|
|
8000,
|
|
|
|
|
@ -49,12 +64,35 @@ public JavaDecoder()
|
|
|
|
|
Format.NOT_SPECIFIED,
|
|
|
|
|
Format.NOT_SPECIFIED)
|
|
|
|
|
};
|
|
|
|
|
inputFormats = supportedInputFormats;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the name of this plugin/codec.
|
|
|
|
|
* @return the name.
|
|
|
|
|
*/
|
|
|
|
|
public String getName()
|
|
|
|
|
{
|
|
|
|
|
return "Speex Decoder";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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(AudioFormat.LINEAR)};
|
|
|
|
|
// mismatch input format
|
|
|
|
|
if (!(in instanceof AudioFormat)
|
|
|
|
|
|| (null == AbstractCodecExt.matches(in, inputFormats)))
|
|
|
|
|
return new Format[0];
|
|
|
|
|
|
|
|
|
|
PLUGIN_NAME = "Speex Decoder";
|
|
|
|
|
return getMatchingOutputFormats(in);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -62,23 +100,20 @@ public JavaDecoder()
|
|
|
|
|
* @param in input format
|
|
|
|
|
* @return array of format 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(
|
|
|
|
|
AudioFormat.LINEAR,
|
|
|
|
|
af.getSampleRate(),
|
|
|
|
|
16,
|
|
|
|
|
af.getChannels(),
|
|
|
|
|
AudioFormat.LITTLE_ENDIAN, //isBigEndian(),
|
|
|
|
|
AudioFormat.SIGNED //isSigned());
|
|
|
|
|
new AudioFormat(
|
|
|
|
|
AudioFormat.LINEAR,
|
|
|
|
|
af.getSampleRate(),
|
|
|
|
|
16,
|
|
|
|
|
af.getChannels(),
|
|
|
|
|
AudioFormat.LITTLE_ENDIAN, //isBigEndian(),
|
|
|
|
|
AudioFormat.SIGNED //isSigned());
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
return supportedOutputFormats;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -93,6 +128,10 @@ public void open()
|
|
|
|
|
public void close()
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize the decoder.
|
|
|
|
|
* @param inFormat the input format.
|
|
|
|
|
*/
|
|
|
|
|
private void initConverter(AudioFormat inFormat)
|
|
|
|
|
{
|
|
|
|
|
lastFormat = inFormat;
|
|
|
|
|
@ -137,7 +176,6 @@ public int process(Buffer inputBuffer, Buffer outputBuffer)
|
|
|
|
|
byte[] inData = (byte[]) inputBuffer.getData();
|
|
|
|
|
|
|
|
|
|
int inpLength = inputBuffer.getLength();
|
|
|
|
|
|
|
|
|
|
int outLength = 0;
|
|
|
|
|
|
|
|
|
|
int inOffset = inputBuffer.getOffset();
|
|
|
|
|
@ -152,172 +190,48 @@ public int process(Buffer inputBuffer, Buffer outputBuffer)
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
decoder.processData(inData, inOffset, inpLength);
|
|
|
|
|
int fullyProcessed =
|
|
|
|
|
decoder.processData(inData, inOffset, inpLength);
|
|
|
|
|
|
|
|
|
|
// sometimes we get more than a frame in single speex packet
|
|
|
|
|
// we must process it all
|
|
|
|
|
while(fullyProcessed != 1)
|
|
|
|
|
{
|
|
|
|
|
fullyProcessed = decoder.processData(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outLength = decoder.getProcessedDataByteSize();
|
|
|
|
|
|
|
|
|
|
byte[] outData = validateByteArraySize(outputBuffer, outLength);
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
|
|
decoder.getProcessedData(outData, outOffset);
|
|
|
|
|
if (outData instanceof byte[])
|
|
|
|
|
{
|
|
|
|
|
out = (byte[]) outData;
|
|
|
|
|
if (out.length < outLength)
|
|
|
|
|
out = null;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
out = null;
|
|
|
|
|
if (out == null)
|
|
|
|
|
out = new byte[outLength];
|
|
|
|
|
|
|
|
|
|
decoder.getProcessedData(out, outOffset);
|
|
|
|
|
|
|
|
|
|
outputBuffer.setData(out);
|
|
|
|
|
outputBuffer.setLength(out.length);
|
|
|
|
|
outputBuffer.setOffset(0);
|
|
|
|
|
outputBuffer.setFormat(outputFormat);
|
|
|
|
|
}
|
|
|
|
|
catch (StreamCorruptedException ex)
|
|
|
|
|
{
|
|
|
|
|
ex.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateOutput(outputBuffer, outputFormat, outLength, 0);
|
|
|
|
|
|
|
|
|
|
return BUFFER_PROCESSED_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// public java.lang.Object[] getControls()
|
|
|
|
|
// {
|
|
|
|
|
// if (controls == null)
|
|
|
|
|
// {
|
|
|
|
|
// controls = new Control[1];
|
|
|
|
|
// controls[0] = new com.sun.media.controls.SilenceSuppressionAdapter(this, false, false);
|
|
|
|
|
// }
|
|
|
|
|
// return (Object[]) controls;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// TODO clean code (remove) array if really not needed
|
|
|
|
|
// static int SpeexSubModeSz[] =
|
|
|
|
|
// {
|
|
|
|
|
// 0, 43, 119, 160,
|
|
|
|
|
// 220, 300, 364, 492,
|
|
|
|
|
// 79, 0, 0, 0,
|
|
|
|
|
// 0, 0, 0, 0};
|
|
|
|
|
// static int SpeexInBandSz[] =
|
|
|
|
|
// {
|
|
|
|
|
// 1, 1, 4, 4,
|
|
|
|
|
// 4, 4, 4, 4,
|
|
|
|
|
// 8, 8, 16, 16,
|
|
|
|
|
// 32, 32, 64, 64};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Counts the samples in given data
|
|
|
|
|
* @param data byte[]
|
|
|
|
|
* @param len int
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
// TODO: clean code (remove) following method if really not used
|
|
|
|
|
|
|
|
|
|
// private static int speex_get_samples(byte[] data, int len)
|
|
|
|
|
// {
|
|
|
|
|
// int bit = 0;
|
|
|
|
|
// int cnt = 0;
|
|
|
|
|
// int off = 0;
|
|
|
|
|
//
|
|
|
|
|
// int c;
|
|
|
|
|
//
|
|
|
|
|
// //DEBU(G "speex_get_samples(%d)\n", len);
|
|
|
|
|
// while ((len * 8 - bit) >= 5) {
|
|
|
|
|
// /* skip wideband frames */
|
|
|
|
|
// off = speex_get_wb_sz_at(data, len, bit);
|
|
|
|
|
// if (off < 0) {
|
|
|
|
|
// //DEBU(G "\tERROR reading wideband frames\n");
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// bit += off;
|
|
|
|
|
//
|
|
|
|
|
// if ((len * 8 - bit) < 5) {
|
|
|
|
|
// //DEBU(G "\tERROR not enough bits left after wb\n");
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// /* get control bits */
|
|
|
|
|
// c = get_n_bits_at(data, 5, bit);
|
|
|
|
|
// //DEBU(G "\tCONTROL: %d at %d\n", c, bit);
|
|
|
|
|
// bit += 5;
|
|
|
|
|
//
|
|
|
|
|
// if (c == 15) {
|
|
|
|
|
// //DEBU(G "\tTERMINATOR\n");
|
|
|
|
|
// break;
|
|
|
|
|
// } else if (c == 14) {
|
|
|
|
|
// /* in-band signal; next 4 bits contain signal id */
|
|
|
|
|
// c = get_n_bits_at(data, 4, bit);
|
|
|
|
|
// bit += 4;
|
|
|
|
|
// //DEBUG "\tIN-BAND %d bits\n", SpeexInBandSz[c]);
|
|
|
|
|
// bit += SpeexInBandSz[c];
|
|
|
|
|
// } else if (c == 13) {
|
|
|
|
|
// /* user in-band; next 5 bits contain msg len */
|
|
|
|
|
// c = get_n_bits_at(data, 5, bit);
|
|
|
|
|
// bit += 5;
|
|
|
|
|
// //DEBUG "\tUSER-BAND %d bytes\n", c);
|
|
|
|
|
// bit += c * 8;
|
|
|
|
|
// } else if (c > 8) {
|
|
|
|
|
// //DEBU(G "\tUNKNOWN sub-mode %d\n", c);
|
|
|
|
|
// break;
|
|
|
|
|
// } else {
|
|
|
|
|
// /* skip number bits for submode (less the 5 control bits) */
|
|
|
|
|
// //DEBU(G "\tSUBMODE %d %d bits\n", c, SpeexSubModeSz[c]);
|
|
|
|
|
// bit += SpeexSubModeSz[c] - 5;
|
|
|
|
|
//
|
|
|
|
|
// cnt += 160; /* new frame */
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// //DEBU(G "\tSAMPLES: %d\n", cnt);
|
|
|
|
|
// return cnt;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// TODO: clean code (remove) following method if really not used - wide band?
|
|
|
|
|
// private static int get_n_bits_at(byte[] data, int n, int bit)
|
|
|
|
|
// {
|
|
|
|
|
// int byteInt = bit / 8; /* byte containing first bit */
|
|
|
|
|
// int rem = 8 - (bit % 8); /* remaining bits in first byte */
|
|
|
|
|
// int ret = 0;
|
|
|
|
|
//
|
|
|
|
|
// if (n <= 0 || n > 8)
|
|
|
|
|
// return 0;
|
|
|
|
|
//
|
|
|
|
|
// if (rem < n)
|
|
|
|
|
// {
|
|
|
|
|
// ret = (data[byteInt] << (n - rem));
|
|
|
|
|
// ret |= (data[byteInt +1] >> (8 - n + rem));
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// {
|
|
|
|
|
// ret = (data[byteInt] >> (rem - n));
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// return (ret & (0xff >> (8 - n)));
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// TODO clean code (remove) array if really not needed
|
|
|
|
|
// static int SpeexWBSubModeSz[] =
|
|
|
|
|
// {
|
|
|
|
|
// 0, 36, 112, 192,
|
|
|
|
|
// 352, 0, 0, 0};
|
|
|
|
|
|
|
|
|
|
// TODO: clean code (remove) following method if really not used - wide band?
|
|
|
|
|
|
|
|
|
|
// private static int speex_get_wb_sz_at(byte[] data, int len, int bit)
|
|
|
|
|
// {
|
|
|
|
|
// int off = bit;
|
|
|
|
|
// int c;
|
|
|
|
|
//
|
|
|
|
|
// /* skip up to two wideband frames */
|
|
|
|
|
// if ( ( (len * 8 - off) >= 5) &&
|
|
|
|
|
// get_n_bits_at(data, 1, off) != 0)
|
|
|
|
|
// {
|
|
|
|
|
// c = get_n_bits_at(data, 3, off + 1);
|
|
|
|
|
// off += SpeexWBSubModeSz[c];
|
|
|
|
|
//
|
|
|
|
|
// if ( ( (len * 8 - off) >= 5) &&
|
|
|
|
|
// get_n_bits_at(data, 1, off) != 0)
|
|
|
|
|
// {
|
|
|
|
|
// c = get_n_bits_at(data, 3, off + 1);
|
|
|
|
|
// off += SpeexWBSubModeSz[c];
|
|
|
|
|
//
|
|
|
|
|
// if ( ( (len * 8 - off) >= 5) &&
|
|
|
|
|
// get_n_bits_at(data, 1, off) != 0)
|
|
|
|
|
// {
|
|
|
|
|
// /* too many in a row */
|
|
|
|
|
// //DEBU(G "\tCORRUPT too many wideband streams in a row\n");
|
|
|
|
|
// return -1;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// }
|
|
|
|
|
// return off - bit;
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|