aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/jspeex.jarbin0 -> 98722 bytes
-rw-r--r--src/net/java/sip/communicator/impl/media/MediaControl.java10
-rw-r--r--src/net/java/sip/communicator/impl/media/MediaUtils.java22
-rw-r--r--src/net/java/sip/communicator/impl/media/codec/Constants.java4
-rw-r--r--src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaDecoder.java290
-rw-r--r--src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaEncoder.java145
6 files changed, 468 insertions, 3 deletions
diff --git a/lib/jspeex.jar b/lib/jspeex.jar
new file mode 100644
index 0000000..0a0079b
--- /dev/null
+++ b/lib/jspeex.jar
Binary files differ
diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java
index 3529df5..a157cc9 100644
--- a/src/net/java/sip/communicator/impl/media/MediaControl.java
+++ b/src/net/java/sip/communicator/impl/media/MediaControl.java
@@ -81,8 +81,8 @@ public class MediaControl
// javax.media.format.AudioFormat.DVI_RTP;
Integer.toString(SdpConstants.DVI4_16000),
// javax.media.format.AudioFormat.ALAW;
- // PCMU is only supported for decoding ... as good as not supported
- //Integer.toString(SdpConstants.PCMA),
+ Integer.toString(SdpConstants.PCMA),
+ Integer.toString(110),
// javax.media.format.AudioFormat.G728_RTP;
Integer.toString(SdpConstants.G728)
// javax.media.format.AudioFormat.G729_RTP
@@ -132,7 +132,11 @@ public class MediaControl
{
"net.java.sip.communicator.impl.media.codec.audio.alaw.JavaEncoder",
"net.java.sip.communicator.impl.media.codec.audio.alaw.DePacketizer",
- "net.java.sip.communicator.impl.media.codec.audio.alaw.Packetizer"
+ "net.java.sip.communicator.impl.media.codec.audio.alaw.Packetizer",
+ "net.java.sip.communicator.impl.media.codec.audio.speex.JavaEncoder",
+ "net.java.sip.communicator.impl.media.codec.audio.speex.JavaDecoder"
+// ,"net.java.sip.communicator.impl.media.codec.audio.ilbc.JavaEncoder",
+// "net.java.sip.communicator.impl.media.codec.audio.ilbc.JavaDecoder"
// "net.java.sip.communicator.impl.media.codec.audio.g729.JavaDecoder",
// "net.java.sip.communicator.impl.media.codec.audio.g729.JavaEncoder",
// "net.java.sip.communicator.impl.media.codec.audio.g729.DePacketizer",
diff --git a/src/net/java/sip/communicator/impl/media/MediaUtils.java b/src/net/java/sip/communicator/impl/media/MediaUtils.java
index de93e37..ad3dfc8 100644
--- a/src/net/java/sip/communicator/impl/media/MediaUtils.java
+++ b/src/net/java/sip/communicator/impl/media/MediaUtils.java
@@ -50,6 +50,12 @@ public class MediaUtils
return AudioFormat.DVI_RTP;
case SdpConstants.PCMA:
return Constants.ALAW_RTP;
+ case 97:
+ return Constants.ILBC_RTP;
+ case 98:
+ return Constants.ILBC_RTP;
+ case 110:
+ return Constants.SPEEX_RTP;
case SdpConstants.G728:
return AudioFormat.G728_RTP;
case SdpConstants.G729:
@@ -129,6 +135,22 @@ public class MediaUtils
{
return Integer.toString(SdpConstants.H261);
}
+ else if (jmfEncoding.equals(Constants.ILBC))
+ {
+ return Integer.toString(97);
+ }
+ else if (jmfEncoding.equals(Constants.ILBC_RTP))
+ {
+ return Integer.toString(97);
+ }
+ else if (jmfEncoding.equals(Constants.SPEEX))
+ {
+ return Integer.toString(110);
+ }
+ else if (jmfEncoding.equals(Constants.SPEEX_RTP))
+ {
+ return Integer.toString(110);
+ }
else
{
return null;
diff --git a/src/net/java/sip/communicator/impl/media/codec/Constants.java b/src/net/java/sip/communicator/impl/media/codec/Constants.java
index 884675e..44c9373 100644
--- a/src/net/java/sip/communicator/impl/media/codec/Constants.java
+++ b/src/net/java/sip/communicator/impl/media/codec/Constants.java
@@ -13,4 +13,8 @@ package net.java.sip.communicator.impl.media.codec;
public class Constants
{
public static final String ALAW_RTP = "ALAW/rtp";
+ public static final String SPEEX_RTP = "speex/rtp";
+ public static final String SPEEX = "speex";
+ public static final String ILBC_RTP = "ilbc/rtp";
+ public static final String ILBC = "ilbc";
}
diff --git a/src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaDecoder.java b/src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaDecoder.java
new file mode 100644
index 0000000..32a1fa3
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaDecoder.java
@@ -0,0 +1,290 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.media.codec.audio.speex;
+
+import java.io.*;
+import javax.media.*;
+import javax.media.format.*;
+
+import org.xiph.speex.*;
+import net.java.sip.communicator.impl.media.codec.*;
+
+/**
+ * Speex to PCM java decoder
+ * @author Damian Minkov
+ **/
+public class JavaDecoder
+ extends com.ibm.media.codec.audio.AudioCodec
+{
+ private Format lastFormat = null;
+ private int numberOfChannels = 0;
+ private SpeexDecoder decoder = null;
+
+ public JavaDecoder()
+ {
+ inputFormats = new Format[]
+ {
+ new AudioFormat(
+ Constants.SPEEX_RTP,
+ 8000,
+ 8,
+ 1,
+ Format.NOT_SPECIFIED,
+ Format.NOT_SPECIFIED
+ )};
+
+ supportedInputFormats = new AudioFormat[]
+ {
+ new AudioFormat(Constants.SPEEX_RTP,
+ 8000,
+ 8,
+ 1,
+ Format.NOT_SPECIFIED,
+ Format.NOT_SPECIFIED)};
+
+ defaultOutputFormats = new AudioFormat[]
+ {
+ new AudioFormat(AudioFormat.LINEAR)};
+
+ PLUGIN_NAME = "Speex Decoder";
+ }
+
+ protected Format[] getMatchingOutputFormats(Format in)
+ {
+ AudioFormat af = (AudioFormat) in;
+
+ supportedOutputFormats = new AudioFormat[]
+ {
+ new AudioFormat(
+ AudioFormat.LINEAR,
+ af.getSampleRate(),
+ 16,
+ af.getChannels(),
+ AudioFormat.LITTLE_ENDIAN, //isBigEndian(),
+ AudioFormat.SIGNED //isSigned());
+ )};
+
+ return supportedOutputFormats;
+
+ }
+
+
+ public void open()
+ {}
+
+ public void close()
+ {}
+
+ private void initConverter(AudioFormat inFormat)
+ {
+ lastFormat = inFormat;
+
+ numberOfChannels = inFormat.getChannels();
+
+ decoder = new SpeexDecoder();
+
+ decoder.init(0,
+ (int) ( (AudioFormat) inFormat).getSampleRate(),
+ inFormat.getChannels(),
+ false);
+ }
+
+ public int process(Buffer inputBuffer, Buffer outputBuffer)
+ {
+ if (!checkInputBuffer(inputBuffer))
+ {
+ return BUFFER_PROCESSED_FAILED;
+ }
+
+ if (isEOM(inputBuffer))
+ {
+ propagateEOM(outputBuffer);
+ return BUFFER_PROCESSED_OK;
+ }
+
+ int channels = ( (AudioFormat) outputFormat).getChannels();
+
+ byte[] inData = (byte[]) inputBuffer.getData();
+
+ int inpLength = inputBuffer.getLength();
+
+ int outLength = 0;
+
+ int inOffset = inputBuffer.getOffset();
+ int outOffset = outputBuffer.getOffset();
+
+ Format newFormat = inputBuffer.getFormat();
+
+ if (lastFormat != newFormat)
+ {
+ initConverter( (AudioFormat) newFormat);
+ }
+
+ try
+ {
+ decoder.processData(inData, inOffset, inpLength);
+ outLength = decoder.getProcessedDataByteSize();
+
+ byte[] outData = validateByteArraySize(outputBuffer, outLength);
+
+ decoder.getProcessedData(outData, outOffset);
+ }
+ 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;
+// }
+
+ 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
+ */
+ 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;
+ }
+
+ 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)));
+ }
+
+ static int SpeexWBSubModeSz[] =
+ {
+ 0, 36, 112, 192,
+ 352, 0, 0, 0};
+
+ 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;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaEncoder.java b/src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaEncoder.java
new file mode 100644
index 0000000..83f42ba
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/codec/audio/speex/JavaEncoder.java
@@ -0,0 +1,145 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.media.codec.audio.speex;
+
+import javax.media.*;
+import javax.media.format.*;
+
+import org.xiph.speex.*;
+import net.java.sip.communicator.impl.media.codec.*;
+
+/**
+ * The Speex Encoder
+ *
+ * @author Damian Minkov
+ */
+public class JavaEncoder
+ extends com.ibm.media.codec.audio.AudioCodec
+{
+ private Format lastFormat = null;
+ private int numberOfChannels = 0;
+
+ private static int FRAME_SIZE = 320;
+
+ private SpeexEncoder encoder = null;
+
+ public JavaEncoder()
+ {
+ supportedInputFormats = new AudioFormat[]
+ {
+ new AudioFormat(
+ AudioFormat.LINEAR,
+ 8000,
+ 16,
+ 1,
+ AudioFormat.LITTLE_ENDIAN, //isBigEndian(),
+ AudioFormat.SIGNED //isSigned());
+ )};
+
+ defaultOutputFormats = new AudioFormat[]
+ {new AudioFormat(Constants.SPEEX_RTP)};
+
+ PLUGIN_NAME = "pcm to speex converter";
+ }
+
+ protected Format[] getMatchingOutputFormats(Format in)
+ {
+ AudioFormat af = (AudioFormat) in;
+
+ supportedOutputFormats = new AudioFormat[]
+ {new AudioFormat(
+ Constants.SPEEX_RTP,
+ af.getSampleRate(),
+ af.getSampleSizeInBits(),
+ af.getChannels(),
+ af.getEndian(),
+ af.getSigned()
+ )};
+
+ return supportedOutputFormats;
+ }
+
+ public void open() throws ResourceUnavailableException
+ {
+
+ }
+
+ public void close()
+ {
+
+ }
+
+ private void initConverter(AudioFormat inFormat)
+ {
+ lastFormat = inFormat;
+ numberOfChannels = inFormat.getChannels();
+
+ encoder = new SpeexEncoder();
+
+ encoder.init(0, 4, (int)inFormat.getSampleRate(), 1);
+ }
+
+ public int process(Buffer inputBuffer, Buffer outputBuffer)
+ {
+ if (!checkInputBuffer(inputBuffer))
+ {
+ return BUFFER_PROCESSED_FAILED;
+ }
+
+ if (isEOM(inputBuffer))
+ {
+ propagateEOM(outputBuffer);
+ return BUFFER_PROCESSED_OK;
+ }
+
+ Format newFormat = inputBuffer.getFormat();
+
+ if (lastFormat != newFormat)
+ {
+ initConverter( (AudioFormat) newFormat);
+ }
+
+ int inpLength = inputBuffer.getLength();
+
+ byte[] inpData = (byte[]) inputBuffer.getData();
+ int inOffset = inputBuffer.getOffset();
+
+ if (inpLength == 0)
+ {
+ return OUTPUT_BUFFER_NOT_FILLED;
+ }
+
+ if ( (inpLength - inOffset) >= FRAME_SIZE)
+ {
+ encoder.processData(inpData, inOffset, FRAME_SIZE);
+ byte[] buff = new byte[encoder.getProcessedDataByteSize()];
+ encoder.getProcessedData(buff, 0);
+
+ byte[] outData = validateByteArraySize(outputBuffer, buff.length);
+
+ System.arraycopy(buff, 0, outData, outputBuffer.getOffset(),
+ buff.length);
+
+ updateOutput(outputBuffer, outputFormat, outData.length, 0);
+
+ if ( (inpLength - inOffset) > FRAME_SIZE)
+ {
+ inputBuffer.setOffset(inOffset + FRAME_SIZE);
+
+ return BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED;
+ }
+ else
+ {
+ return BUFFER_PROCESSED_OK;
+ }
+ }
+ else
+ {
+ return OUTPUT_BUFFER_NOT_FILLED;
+ }
+ }
+}