diff --git a/lib/jmf-all/jmf.jar b/lib/jmf-all/jmf.jar index 0e3905391..0ab593a86 100644 Binary files a/lib/jmf-all/jmf.jar and b/lib/jmf-all/jmf.jar differ diff --git a/lib/jmf-lin/jmf.jar b/lib/jmf-lin/jmf.jar index ada9bb09e..07240d660 100644 Binary files a/lib/jmf-lin/jmf.jar and b/lib/jmf-lin/jmf.jar differ diff --git a/lib/jmf-sol/jmf.jar b/lib/jmf-sol/jmf.jar index cdca61ba2..bb250c5c4 100644 Binary files a/lib/jmf-sol/jmf.jar and b/lib/jmf-sol/jmf.jar differ diff --git a/lib/jmf-win/jmf.jar b/lib/jmf-win/jmf.jar index f3b6b73ed..154ea705f 100644 Binary files a/lib/jmf-win/jmf.jar and b/lib/jmf-win/jmf.jar differ diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java index eeeb6551a..3529df533 100644 --- a/src/net/java/sip/communicator/impl/media/MediaControl.java +++ b/src/net/java/sip/communicator/impl/media/MediaControl.java @@ -26,6 +26,7 @@ * * @author Martin Andre * @author Emil Ivov + * @author Damian Minkov */ public class MediaControl { @@ -124,6 +125,20 @@ public class MediaControl private static final String DEBUG_DATA_SOURCE_URL_PROPERTY_NAME = "net.java.sip.communicator.impl.media.DEBUG_DATA_SOURCE_URL"; + /** + * + */ + private static String[] customCodecs = new String[] + { + "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.g729.JavaDecoder", +// "net.java.sip.communicator.impl.media.codec.audio.g729.JavaEncoder", +// "net.java.sip.communicator.impl.media.codec.audio.g729.DePacketizer", +// "net.java.sip.communicator.impl.media.codec.audio.g729.Packetizer" + }; + /** * The default constructor. */ @@ -396,6 +411,9 @@ public void initDebugDataSource(String debugMediaSource) private void initProcessor(DataSource dataSource) throws MediaException { + // register our custom codecs + registerCustomCodecs(); + try { try @@ -960,4 +978,44 @@ public void stopProcessingMedia(Object reader) if(processorReaders.contains(reader)) processorReaders.remove(reader); } + + /** + * Register in JMF the custom codecs we provide + */ + private void registerCustomCodecs() + { + for (int i = 0; i < customCodecs.length; i++) + { + String className = customCodecs[i]; + try + { + + Class pic = Class.forName(className); + Object instance = pic.newInstance(); + + boolean result = + PlugInManager.addPlugIn( + className, + ( (Codec) instance).getSupportedInputFormats(), + ( (Codec) instance).getSupportedOutputFormats(null), + PlugInManager.CODEC); + logger.debug("Codec : " + className + + " is succsefully registered : " + result); + } + catch (Exception ex) + { + logger.debug("Codec : " + className + + " is NOT succsefully registered"); + } + } + + try + { + PlugInManager.commit(); + } + catch (IOException ex) + { + logger.error("Cannot commit to PlugInManager", ex); + } + } } diff --git a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java index 18cf80570..52fd18f25 100644 --- a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java +++ b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java @@ -14,13 +14,8 @@ import net.java.sip.communicator.impl.media.device.*; import net.java.sip.communicator.service.media.*; import net.java.sip.communicator.service.media.event.*; -import net.java.sip.communicator.service.netaddr.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; -import com.sun.media.util.Registry; -import javax.media.PlugInManager; -import javax.media.Format; -import javax.media.Codec; /** * The service is meant to be a wrapper of media libraries such as JMF, @@ -265,16 +260,6 @@ public static void main(String[] args) MediaServiceImpl msimpl = new MediaServiceImpl(); msimpl.start(); - String className = "classname"; - Class pic = Class.forName(className); - Object instance = pic.newInstance(); - - PlugInManager.addPlugIn( - className, - ((Codec)instance).getSupportedInputFormats(), - ((Codec)instance).getSupportedOutputFormats(null), - PlugInManager.CODEC); - System.out.println("done"); } } diff --git a/src/net/java/sip/communicator/impl/media/MediaUtils.java b/src/net/java/sip/communicator/impl/media/MediaUtils.java index 74347456b..de93e37e0 100644 --- a/src/net/java/sip/communicator/impl/media/MediaUtils.java +++ b/src/net/java/sip/communicator/impl/media/MediaUtils.java @@ -9,6 +9,7 @@ import javax.media.format.*; import javax.sdp.*; import java.util.*; +import net.java.sip.communicator.impl.media.codec.*; /** * Implements static utility methods used by media classes. @@ -48,7 +49,7 @@ public static String sdpToJmfEncoding(String sdpEncodingStr) case SdpConstants.DVI4_16000: return AudioFormat.DVI_RTP; case SdpConstants.PCMA: - return AudioFormat.ALAW; + return Constants.ALAW_RTP; case SdpConstants.G728: return AudioFormat.G728_RTP; case SdpConstants.G729: @@ -84,6 +85,10 @@ else if (jmfEncoding.equals(AudioFormat.ULAW_RTP)) { return Integer.toString(SdpConstants.PCMU); } + else if (jmfEncoding.equals(Constants.ALAW_RTP)) + { + return Integer.toString(SdpConstants.PCMA); + } else if (jmfEncoding.equals(AudioFormat.GSM_RTP)) { return Integer.toString(SdpConstants.GSM); diff --git a/src/net/java/sip/communicator/impl/media/codec/Constants.java b/src/net/java/sip/communicator/impl/media/codec/Constants.java new file mode 100644 index 000000000..884675e2a --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/codec/Constants.java @@ -0,0 +1,16 @@ +/* + * 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; + +/** + * Constants needed in codecs impl. + * @author Damian Minkov + */ +public class Constants +{ + public static final String ALAW_RTP = "ALAW/rtp"; +} diff --git a/src/net/java/sip/communicator/impl/media/codec/audio/alaw/DePacketizer.java b/src/net/java/sip/communicator/impl/media/codec/audio/alaw/DePacketizer.java new file mode 100644 index 000000000..ca867e0fa --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/codec/audio/alaw/DePacketizer.java @@ -0,0 +1,113 @@ +/* + * 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.alaw; + +import javax.media.*; +import javax.media.format.*; + +import com.sun.media.codec.audio.*; +import net.java.sip.communicator.impl.media.codec.*; + +/** + * DePacketizer for ALAW codec + * @author Damian Minkov + */ +public class DePacketizer + extends AudioCodec +{ + /** + * Creates DePacketizer + */ + public DePacketizer() + { + inputFormats = new Format[]{new AudioFormat(Constants.ALAW_RTP)}; + } + + /** + * Returns the name of the DePacketizer + * @return String + */ + public String getName() + { + return "ALAW DePacketizer"; + } + + /** + * Returns the supported output formats + * @param in Format + * @return Format[] + */ + public Format[] getSupportedOutputFormats(Format in) + { + + if (in == null) + { + return new Format[]{new AudioFormat(AudioFormat.ALAW)}; + } + + if (matches(in, inputFormats) == null) + { + return new Format[1]; + } + + if (! (in instanceof AudioFormat)) + { + return new Format[]{new AudioFormat(AudioFormat.ALAW)}; + } + + AudioFormat af = (AudioFormat) in; + return new Format[] + { + new AudioFormat( + AudioFormat.ALAW, + af.getSampleRate(), + af.getSampleSizeInBits(), + af.getChannels()) + }; + } + + /** + * Initializes the codec. + */ + public void open() + {} + + /** + * Clean up + */ + public void close() + {} + + /** + * decode the buffer + * @param inputBuffer Buffer + * @param outputBuffer Buffer + * @return int + */ + public int process(Buffer inputBuffer, Buffer outputBuffer) + { + + if (!checkInputBuffer(inputBuffer)) + { + return BUFFER_PROCESSED_FAILED; + } + + if (isEOM(inputBuffer)) + { + propagateEOM(outputBuffer); + return BUFFER_PROCESSED_OK; + } + + Object outData = outputBuffer.getData(); + outputBuffer.setData(inputBuffer.getData()); + inputBuffer.setData(outData); + outputBuffer.setLength(inputBuffer.getLength()); + outputBuffer.setFormat(outputFormat); + outputBuffer.setOffset(inputBuffer.getOffset()); + return BUFFER_PROCESSED_OK; + } +} diff --git a/src/net/java/sip/communicator/impl/media/codec/audio/alaw/JavaEncoder.java b/src/net/java/sip/communicator/impl/media/codec/audio/alaw/JavaEncoder.java new file mode 100644 index 000000000..a49684b0a --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/codec/audio/alaw/JavaEncoder.java @@ -0,0 +1,342 @@ +/* + * 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.alaw; + +import javax.media.*; +import javax.media.format.*; + +/** + * The ALAW Encoder + * + * @author Damian Minkov + */ +public class JavaEncoder + extends com.ibm.media.codec.audio.AudioCodec +{ + private Format lastFormat = null; + private int numberOfInputChannels; + private int numberOfOutputChannels = 1; + private boolean downmix = false; + private int inputSampleSize; + private boolean bigEndian = false; + + public JavaEncoder() + { + supportedInputFormats = new AudioFormat[] + { + new AudioFormat( + AudioFormat.LINEAR, + Format.NOT_SPECIFIED, + 16, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + ), + new AudioFormat( + AudioFormat.LINEAR, + Format.NOT_SPECIFIED, + 16, + 2, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + ), + new AudioFormat( + AudioFormat.LINEAR, + Format.NOT_SPECIFIED, + 8, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + ), + new AudioFormat( + AudioFormat.LINEAR, + Format.NOT_SPECIFIED, + 8, + 2, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + )}; // support 1/2 channels and 8/16 bit samples + + defaultOutputFormats = new AudioFormat[] + { + new AudioFormat( + AudioFormat.ALAW, + 8000, + 8, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + )}; + PLUGIN_NAME = "pcm to alaw converter"; + } + + protected Format[] getMatchingOutputFormats(Format in) + { + + AudioFormat inFormat = (AudioFormat) in; + int channels = inFormat.getChannels(); + int sampleRate = (int) (inFormat.getSampleRate()); + + if (channels == 2) + { + supportedOutputFormats = new AudioFormat[] + { + new AudioFormat( + AudioFormat.ALAW, + sampleRate, + 8, + 2, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + ), + new AudioFormat( + AudioFormat.ALAW, + sampleRate, + 8, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + )}; + + } + else + { + supportedOutputFormats = new AudioFormat[] + { + new AudioFormat( + AudioFormat.ALAW, + sampleRate, + 8, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED + )}; + + } + + return supportedOutputFormats; + } + + public void open() throws ResourceUnavailableException + {} + + public void close() + {} + + private int calculateOutputSize(int inputLength) + { + if (inputSampleSize == 16) + { + inputLength /= 2; + } + + if (downmix) + { + inputLength /= 2; + } + + return inputLength; + } + + /** + * Init the converter to the new format + * @param inFormat AudioFormat + */ + private void initConverter(AudioFormat inFormat) + { + + lastFormat = inFormat; + numberOfInputChannels = inFormat.getChannels(); + if (outputFormat != null) + { + numberOfOutputChannels = outputFormat.getChannels(); + } + inputSampleSize = inFormat.getSampleSizeInBits(); + + bigEndian = inFormat.getEndian()==AudioFormat.BIG_ENDIAN; + + if ( (numberOfInputChannels == 2) && (numberOfOutputChannels == 1)) + { + downmix = true; + } + else + { + downmix = false; + } + + } + + /** + * Encodes the input buffer passing it to the output one + * @param inputBuffer Buffer + * @param outputBuffer Buffer + * @return int + */ + 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(); + int outLength = calculateOutputSize(inputBuffer.getLength()); + + byte[] inpData = (byte[]) inputBuffer.getData(); + byte[] outData = validateByteArraySize(outputBuffer, outLength); + + pcm162alaw( + inpData, inputBuffer.getOffset(), outData, 0, outData.length, bigEndian); + + updateOutput(outputBuffer, outputFormat, outLength, 0); + return BUFFER_PROCESSED_OK; + + } + + /* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + private static final byte QUANT_MASK = 0xf; /* Quantization field mask. */ + private static final byte SEG_SHIFT = 4; + /* Left shift for segment number. */ + private static final short[] seg_end = + { + 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF + }; + + private static byte linear2alaw(short pcm_val) + /* 2's complement (16-bit range) */ + { + byte mask; + byte seg = 8; + byte aval; + + if (pcm_val >= 0) + { + mask = (byte) 0xD5; /* sign (7th) bit = 1 */ + } + else + { + mask = 0x55; /* sign bit = 0 */ + pcm_val = (short) ( -pcm_val - 8); + } + + /* Convert the scaled magnitude to segment number. */ + for (int i = 0; i < 8; i++) + { + if (pcm_val <= seg_end[i]) + { + seg = (byte) i; + break; + } + } + + /* Combine the sign, segment, and quantization bits. */ + if (seg >= 8) /* out of range, return maximum value. */ + { + return (byte) ( (0x7F ^ mask) & 0xFF); + } + else + { + aval = (byte) (seg << SEG_SHIFT); + if (seg < 2) + { + aval |= (pcm_val >> 4) & QUANT_MASK; + } + else + { + aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; + } + return (byte) ( (aval ^ mask) & 0xFF); + } + } + + /** + * Converts the input buffer to the otput one using the alaw codec + * @param inBuffer byte[] + * @param inByteOffset int + * @param outBuffer byte[] + * @param outByteOffset int + * @param sampleCount int + * @param bigEndian boolean + */ + private static void pcm162alaw(byte[] inBuffer, int inByteOffset, + byte[] outBuffer, int outByteOffset, + int sampleCount, boolean bigEndian) + { + int shortIndex = inByteOffset; + int alawIndex = outByteOffset; + if (bigEndian) + { + while (sampleCount > 0) + { + outBuffer[alawIndex++] = linear2alaw + (bytesToShort16(inBuffer[shortIndex], + inBuffer[shortIndex + 1])); + shortIndex++; + shortIndex++; + sampleCount--; + } + } + else + { + while (sampleCount > 0) + { + outBuffer[alawIndex++] = linear2alaw + (bytesToShort16(inBuffer[shortIndex + 1], + inBuffer[shortIndex])); + shortIndex++; + shortIndex++; + sampleCount--; + } + } + } + + /** + * Converts the 2 bytes to the corresponding short value + * @param highByte byte + * @param lowByte byte + * @return short + */ + private static short bytesToShort16(byte highByte, byte lowByte) + { + return (short) ( (highByte << 8) | (lowByte & 0xFF)); + } +} diff --git a/src/net/java/sip/communicator/impl/media/codec/audio/alaw/Packetizer.java b/src/net/java/sip/communicator/impl/media/codec/audio/alaw/Packetizer.java new file mode 100644 index 000000000..ba28ac13a --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/codec/audio/alaw/Packetizer.java @@ -0,0 +1,147 @@ +/* + * 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.alaw; + +import javax.media.*; +import javax.media.format.*; + +import com.ibm.media.codec.audio.*; +import net.java.sip.communicator.impl.media.codec.*; + +/** + * Packetizer for ALAW codec + * @author Damian Minkov + */ +public class Packetizer + extends AudioPacketizer +{ + public Packetizer() + { + packetSize = 480; + supportedInputFormats = new AudioFormat[] + { + new AudioFormat( + AudioFormat.ALAW, + Format.NOT_SPECIFIED, + 8, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED, + 8, + Format.NOT_SPECIFIED, + Format.byteArray + ) + }; + defaultOutputFormats = new AudioFormat[] + { + new AudioFormat( + AudioFormat.ALAW, + Format.NOT_SPECIFIED, + 8, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED, + 8, + Format.NOT_SPECIFIED, + Format.byteArray + ) + }; + + PLUGIN_NAME = "Mu-Law Packetizer"; + + } + + protected Format[] getMatchingOutputFormats(Format in) + { + + AudioFormat af = (AudioFormat) in; + + supportedOutputFormats = new AudioFormat[] + { + new AudioFormat( + Constants.ALAW_RTP, + af.getSampleRate(), + 8, + 1, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED, + 8, + Format.NOT_SPECIFIED, + Format.byteArray + ) + }; + return supportedOutputFormats; + } + + public void open() throws ResourceUnavailableException + { + setPacketSize(packetSize); + reset(); + } + + public java.lang.Object[] getControls() + { + if (controls == null) + { + controls = new Control[1]; + controls[0] = new PacketSizeAdapter(this, packetSize, true); + } + return (Object[]) controls; + } + + public synchronized void setPacketSize(int newPacketSize) + { + packetSize = newPacketSize; + + sample_count = packetSize; + + if (history == null) + { + history = new byte[packetSize]; + return; + } + + if (packetSize > history.length) + { + byte[] newHistory = new byte[packetSize]; + System.arraycopy(history, 0, newHistory, 0, historyLength); + history = newHistory; + } + } + +} + +class PacketSizeAdapter + extends com.sun.media.controls.PacketSizeAdapter +{ + public PacketSizeAdapter(Codec newOwner, int newPacketSize, + boolean newIsSetable) + { + super(newOwner, newPacketSize, newIsSetable); + } + + public int setPacketSize(int numBytes) + { + + int numOfPackets = numBytes; + + if (numOfPackets < 10) + { + numOfPackets = 10; + } + + if (numOfPackets > 8000) + { + numOfPackets = 8000; + } + packetSize = numOfPackets; + + ( (Packetizer) owner).setPacketSize(packetSize); + + return packetSize; + } +}