package org.red5.app.sip.trancoders; import org.slf4j.Logger; import org.red5.logging.Red5LoggerFactory; import org.red5.app.sip.codecs.Codec; import org.red5.app.sip.codecs.asao.ByteStream; import org.red5.app.sip.codecs.asao.Decoder; import org.red5.app.sip.codecs.asao.DecoderMap; import org.red5.app.sip.stream.RtpStreamSender; public class NellyToPcmTranscoder implements Transcoder { protected static Logger log = Red5LoggerFactory.getLogger( NellyToPcmTranscoder.class, "sip" ); private static final int NELLYMOSER_DECODED_PACKET_SIZE = 256; private static final int NELLYMOSER_ENCODED_PACKET_SIZE = 64; private Codec sipCodec = null; // Sip codec to be used on audio session private Decoder decoder; private DecoderMap decoderMap; float[] tempBuffer; // Temporary buffer with received PCM audio from FlashPlayer. int tempBufferRemaining = 0; // Floats remaining on temporary buffer. float[] encodingBuffer; // Encoding buffer used to encode to final codec format; int encodingOffset = 0; // Offset of encoding buffer. boolean asao_buffer_processed = false; // Indicates whether the current asao buffer was processed. boolean hasInitilializedBuffers = false; // Indicates whether the handling buffers have already been initialized. public NellyToPcmTranscoder(Codec sipCodec) { this.sipCodec = sipCodec; decoder = new Decoder(); decoderMap = null; } public int getOutgoingEncodedFrameSize() { return sipCodec.getOutgoingEncodedFrameSize(); } public int getCodecId() { return sipCodec.getCodecId(); } public int getOutgoingPacketization() { return sipCodec.getOutgoingPacketization(); } private int fillRtpPacketBuffer(byte[] asaoBuffer, byte[] transcodedData, int dataOffset) { int copyingSize = 0; int finalCopySize = 0; byte[] codedBuffer = new byte[sipCodec.getOutgoingEncodedFrameSize()]; try { if ((tempBufferRemaining + encodingOffset) >= sipCodec.getOutgoingDecodedFrameSize()) { copyingSize = encodingBuffer.length - encodingOffset; System.arraycopy(tempBuffer, tempBuffer.length-tempBufferRemaining, encodingBuffer, encodingOffset, copyingSize); encodingOffset = sipCodec.getOutgoingDecodedFrameSize(); tempBufferRemaining -= copyingSize; finalCopySize = sipCodec.getOutgoingDecodedFrameSize(); } else { if (tempBufferRemaining > 0) { System.arraycopy(tempBuffer, tempBuffer.length - tempBufferRemaining, encodingBuffer, encodingOffset, tempBufferRemaining); encodingOffset += tempBufferRemaining; finalCopySize += tempBufferRemaining; tempBufferRemaining = 0; } // Decode new asao packet. asao_buffer_processed = true; ByteStream audioStream = new ByteStream( asaoBuffer, 1, NELLYMOSER_ENCODED_PACKET_SIZE ); decoderMap = decoder.decode( decoderMap, audioStream.bytes, 1, tempBuffer, 0 ); //tempBuffer = ResampleUtils.normalize(tempBuffer, 256); // normalise volume tempBufferRemaining = tempBuffer.length; if ( tempBuffer.length <= 0 ) { log.error("Asao decoder Error." ); } // Try to complete the encodingBuffer with necessary data. if ( ( encodingOffset + tempBufferRemaining ) > sipCodec.getOutgoingDecodedFrameSize() ) { copyingSize = encodingBuffer.length - encodingOffset; } else { copyingSize = tempBufferRemaining; } System.arraycopy(tempBuffer, 0, encodingBuffer, encodingOffset, copyingSize); encodingOffset += copyingSize; tempBufferRemaining -= copyingSize; finalCopySize += copyingSize; } if (encodingOffset == encodingBuffer.length) { int encodedBytes = sipCodec.pcmToCodec( encodingBuffer, codedBuffer ); if ( encodedBytes == sipCodec.getOutgoingEncodedFrameSize() ) { System.arraycopy(codedBuffer, 0, transcodedData, dataOffset, codedBuffer.length); } else { //println( "fillRtpPacketBuffer", "Failure encoding buffer." ); } } } catch ( Exception e ) { log.error("Exception - " + e.toString()); e.printStackTrace(); } return finalCopySize; } public void transcode(byte[] asaoBuffer, int offset, int num, byte[] transcodedData, int dataOffset, RtpStreamSender rtpSender) { asao_buffer_processed = false; if (!hasInitilializedBuffers) { tempBuffer = new float[NELLYMOSER_DECODED_PACKET_SIZE]; encodingBuffer = new float[sipCodec.getOutgoingDecodedFrameSize()]; hasInitilializedBuffers = true; } if (num > 0) { do { int encodedBytes = fillRtpPacketBuffer( asaoBuffer, transcodedData, dataOffset ); //println( "send", sipCodec.getCodecName() + " encoded " + encodedBytes + " bytes." ); if ( encodedBytes == 0 ) { break; } if ( encodingOffset == sipCodec.getOutgoingDecodedFrameSize() ) { // System.out.println("Send this audio to asterisk."); rtpSender.sendTranscodedData(); encodingOffset = 0; } //println( "send", "asao_buffer_processed = [" // + asao_buffer_processed + "] ." ); } while ( !asao_buffer_processed ); } else if ( num < 0 ) { log.debug("Closing" ); } } public void transcode(byte[] codedBuffer) { // TODO Auto-generated method stub } public int getIncomingEncodedFrameSize() { // TODO Auto-generated method stub return 0; } }