package org.red5.app.sip.trancoders;
import org.slf4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.net.rtmp.event.AudioData;
import org.red5.app.sip.codecs.Codec;
import org.red5.app.sip.codecs.asao.ByteStream;
import org.red5.app.sip.codecs.asao.CodecImpl;
import org.red5.app.sip.stream.RtpStreamSender;
public class PcmToNellyTranscoder implements Transcoder {
protected static Logger log = Red5LoggerFactory.getLogger(PcmToNellyTranscoder.class, "sip");
private static final int NELLYMOSER_DECODED_PACKET_SIZE = 256;
private static final int NELLYMOSER_ENCODED_PACKET_SIZE = 64;
private static final int NELLYMOSER_CODEC_ID = 82;
private float[] encoderMap;
private Codec audioCodec = null;
private float[] tempBuffer; // Temporary buffer with PCM audio to be sent to FlashPlayer.
private int tempBufferOffset = 0;
private final TranscodedAudioDataListener listener;
private long start = 0;
public PcmToNellyTranscoder(Codec audioCodec, TranscodedAudioDataListener listener) {
this.audioCodec = audioCodec;
this.listener = listener;
encoderMap = new float[64];
tempBuffer = new float[NELLYMOSER_DECODED_PACKET_SIZE];
start = System.currentTimeMillis();
}
/**
* Fills the tempBuffer with necessary PCM's floats and encodes
* the audio to be sent to FlashPlayer.
*/
private void forwardAudioToFlashPlayer(float[] pcmBuffer) {
int pcmBufferOffset = 0;
int copySize = 0;
boolean pcmBufferProcessed = false;
do {
if ((tempBuffer.length - tempBufferOffset) <= (pcmBuffer.length - pcmBufferOffset)) {
copySize = tempBuffer.length - tempBufferOffset;
}
else {
copySize = pcmBuffer.length - pcmBufferOffset;
}
System.arraycopy( pcmBuffer, pcmBufferOffset, tempBuffer, tempBufferOffset, copySize);
tempBufferOffset += copySize;
pcmBufferOffset += copySize;
if (tempBufferOffset == NELLYMOSER_DECODED_PACKET_SIZE) {
ByteStream encodedStream = new ByteStream(NELLYMOSER_ENCODED_PACKET_SIZE);
encoderMap = CodecImpl.encode(encoderMap, tempBuffer, encodedStream.bytes);
pushAudio(encodedStream.bytes);
tempBufferOffset = 0;
}
if ( pcmBufferOffset == pcmBuffer.length ) {
pcmBufferProcessed = true;
}
}
while (!pcmBufferProcessed);
}
public void transcode(byte[] codedBuffer) {
float[] decodingBuffer = new float[codedBuffer.length];
int decodedBytes = audioCodec.codecToPcm(codedBuffer, decodingBuffer);
// log.debug("encodedBytes = " + decodedBytes + ", incomingDecodedFrameSize = " +
// audioCodec.getIncomingDecodedFrameSize() + "." );
if (decodedBytes == audioCodec.getIncomingDecodedFrameSize()) {
forwardAudioToFlashPlayer(decodingBuffer);
}
else {
log.warn("Failure decoding buffer." );
}
}
private void pushAudio(byte[] audio) {
IoBuffer buffer = IoBuffer.allocate(1024);
buffer.setAutoExpand(true);
buffer.clear();
buffer.put((byte) NELLYMOSER_CODEC_ID);
byte[] copy = new byte[audio.length];
System.arraycopy(audio, 0, copy, 0, audio.length );
buffer.put(copy);
buffer.flip();
AudioData audioData = new AudioData(buffer);
audioData.setTimestamp((int)(System.currentTimeMillis() - start) );
listener.handleTranscodedAudioData(audioData);
}
public int getIncomingEncodedFrameSize() {
return audioCodec.getIncomingEncodedFrameSize();
}
public void transcode(byte[] asaoBuffer, int offset, int num,
byte[] transcodedData, int dataOffset, RtpStreamSender rtpSender) {
// TODO Auto-generated method stub
}
public int getCodecId() {
// TODO Auto-generated method stub
return 0;
}
public int getOutgoingEncodedFrameSize() {
// TODO Auto-generated method stub
return 0;
}
public int getOutgoingPacketization() {
// TODO Auto-generated method stub
return 0;
}
}