Support to fix speex wideband
This commit is contained in:
parent
4a92404295
commit
a86c7e6186
@ -19,12 +19,7 @@
|
||||
**/
|
||||
package org.bigbluebutton.voiceconf.red5.media;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.net.DatagramSocket;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.FlashToSipTranscoder;
|
||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.TranscodedAudioDataListener;
|
||||
@ -41,12 +36,7 @@ import org.slf4j.Logger;
|
||||
public class FlashToSipAudioStream {
|
||||
private final static Logger log = Red5LoggerFactory.getLogger(FlashToSipAudioStream.class, "sip");
|
||||
|
||||
private final PipedOutputStream streamFromFlash;
|
||||
private PipedInputStream streamToSip;
|
||||
|
||||
private final Executor exec = Executors.newSingleThreadExecutor();
|
||||
private Runnable audioDataProcessor;
|
||||
private volatile boolean processAudioData = false;
|
||||
|
||||
|
||||
private final FlashToSipTranscoder transcoder;
|
||||
private IStreamListener mInputListener;
|
||||
@ -54,24 +44,23 @@ public class FlashToSipAudioStream {
|
||||
private final SipConnectInfo connInfo;
|
||||
private String talkStreamName;
|
||||
private RtpStreamSender rtpSender;
|
||||
|
||||
private TranscodedAudioListener transcodedAudioListener;
|
||||
private volatile boolean processAudioData = false;
|
||||
|
||||
public FlashToSipAudioStream(final FlashToSipTranscoder transcoder, DatagramSocket srcSocket, SipConnectInfo connInfo) {
|
||||
this.transcoder = transcoder;
|
||||
this.srcSocket = srcSocket;
|
||||
this.connInfo = connInfo;
|
||||
talkStreamName = "microphone_" + System.currentTimeMillis();
|
||||
streamFromFlash = new PipedOutputStream();
|
||||
try {
|
||||
streamToSip = new PipedInputStream(streamFromFlash);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
transcodedAudioListener = new TranscodedAudioListener();
|
||||
transcoder.setTranscodedAudioListener(transcodedAudioListener);
|
||||
transcoder.setProcessAudioData(processAudioData);
|
||||
}
|
||||
|
||||
public void start(IBroadcastStream broadcastStream, IScope scope) throws StreamException {
|
||||
log.debug("startTranscodingStream({},{})", broadcastStream.getPublishedName(), scope.getName());
|
||||
|
||||
processAudioData = true;
|
||||
transcoder.setProcessAudioData(processAudioData);
|
||||
mInputListener = new IStreamListener() {
|
||||
public void packetReceived(IBroadcastStream broadcastStream, IStreamPacket packet) {
|
||||
IoBuffer buf = packet.getData();
|
||||
@ -85,12 +74,8 @@ public class FlashToSipAudioStream {
|
||||
|
||||
if (packet instanceof AudioData) {
|
||||
byte[] data = SerializeUtils.ByteBufferToByteArray(buf);
|
||||
try {
|
||||
streamFromFlash.write(data, 1, data.length-1);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (data.length > 20) //==Don't send silence data whose data length is 11
|
||||
transcoder.handlePacket(data, 1, data.length-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -99,39 +84,9 @@ public class FlashToSipAudioStream {
|
||||
rtpSender = new RtpStreamSender(srcSocket, connInfo);
|
||||
rtpSender.connect();
|
||||
|
||||
processAudioData = true;
|
||||
audioDataProcessor = new Runnable() {
|
||||
public void run() {
|
||||
processAudioData();
|
||||
}
|
||||
};
|
||||
exec.execute(audioDataProcessor);
|
||||
|
||||
}
|
||||
|
||||
private void processAudioData() {
|
||||
int len = 64;
|
||||
byte[] nellyAudio = new byte[len];
|
||||
int remaining = len;
|
||||
int offset = 0;
|
||||
TranscodedAudioListener transcodedAudioListener = new TranscodedAudioListener();
|
||||
while (processAudioData) {
|
||||
try {
|
||||
int bytesRead = streamToSip.read(nellyAudio, offset, remaining);
|
||||
remaining -= bytesRead;
|
||||
if (remaining == 0) {
|
||||
remaining = len;
|
||||
offset = 0;
|
||||
transcoder.transcode(nellyAudio, 0, nellyAudio.length, transcodedAudioListener);
|
||||
} else {
|
||||
offset += bytesRead;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop(IBroadcastStream broadcastStream, IScope scope) {
|
||||
broadcastStream.removeStreamListener(mInputListener);
|
||||
if (broadcastStream != null) {
|
||||
@ -139,6 +94,7 @@ public class FlashToSipAudioStream {
|
||||
broadcastStream.close();
|
||||
}
|
||||
processAudioData = false;
|
||||
transcoder.setProcessAudioData(processAudioData);
|
||||
srcSocket.close();
|
||||
}
|
||||
|
||||
@ -146,7 +102,7 @@ public class FlashToSipAudioStream {
|
||||
return talkStreamName;
|
||||
}
|
||||
|
||||
private class TranscodedAudioListener implements TranscodedAudioDataListener {
|
||||
public class TranscodedAudioListener implements TranscodedAudioDataListener {
|
||||
@Override
|
||||
public void handleTranscodedAudioData(byte[] audioData, long timestamp) {
|
||||
if (audioData != null && processAudioData) {
|
||||
|
@ -60,12 +60,7 @@ public class RtpStreamReceiver {
|
||||
}
|
||||
|
||||
private void initializeSocket() {
|
||||
/* try {
|
||||
rtpSocket.getDatagramSocket().setSoTimeout(SO_TIMEOUT);
|
||||
} catch (SocketException e1) {
|
||||
log.warn("SocketException while setting socket block time.");
|
||||
}
|
||||
*/ }
|
||||
}
|
||||
|
||||
public void start() {
|
||||
receivePackets = true;
|
||||
@ -91,9 +86,6 @@ public class RtpStreamReceiver {
|
||||
try {
|
||||
rtpSocket.receive(rtpPacket);
|
||||
packetReceivedCounter++;
|
||||
// log.debug("Received packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
||||
// + "][rtpTS=" + rtpPacket.getTimestamp() + ",lastTS=" + lastPacketTimestamp + "][port=" + rtpSocket.getDatagramSocket().getLocalPort() + "]");
|
||||
|
||||
if (shouldDropDelayedPacket(rtpPacket)) {
|
||||
continue;
|
||||
}
|
||||
@ -107,11 +99,9 @@ public class RtpStreamReceiver {
|
||||
+ "][rtpTS=" + rtpPacket.getTimestamp() + ",lastTS=" + lastPacketTimestamp + "][port=" + rtpSocket.getDatagramSocket().getLocalPort() + "]");
|
||||
} else {
|
||||
if (shouldHandlePacket(rtpPacket)) {
|
||||
// log.debug("Handling packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
||||
// + "][rtpTS=" + rtpPacket.getTimestamp() + ",lastTS=" + lastPacketTimestamp + "][port=" + rtpSocket.getDatagramSocket().getLocalPort() + "]");
|
||||
lastSequenceNumber = rtpPacket.getSeqNum();
|
||||
lastPacketTimestamp = rtpPacket.getTimestamp();
|
||||
processRtpPacket(internalBuffer, RTP_HEADER_SIZE, payloadLength);
|
||||
processRtpPacket(internalBuffer, RTP_HEADER_SIZE, rtpPacket.getPayloadLength());
|
||||
} else {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("Corrupt packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
||||
|
@ -19,12 +19,7 @@
|
||||
**/
|
||||
package org.bigbluebutton.voiceconf.red5.media;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.net.DatagramSocket;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.SipToFlashTranscoder;
|
||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.TranscodedAudioDataListener;
|
||||
@ -38,15 +33,11 @@ import org.red5.server.stream.BroadcastScope;
|
||||
import org.red5.server.stream.IBroadcastScope;
|
||||
import org.red5.server.stream.IProviderService;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpStreamReceiverListener {
|
||||
final private Logger log = Red5LoggerFactory.getLogger(SipToFlashAudioStream.class, "sip");
|
||||
|
||||
private final PipedOutputStream streamFromSip;
|
||||
private PipedInputStream streamToFlash;
|
||||
|
||||
private final Executor exec = Executors.newSingleThreadExecutor();
|
||||
private Runnable audioDataProcessor;
|
||||
// private Runnable audioDataProcessor;
|
||||
private volatile boolean processAudioData = false;
|
||||
|
||||
private AudioBroadcastStream audioBroadcastStream;
|
||||
@ -72,23 +63,26 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
||||
};
|
||||
|
||||
public SipToFlashAudioStream(IScope scope, SipToFlashTranscoder transcoder, DatagramSocket socket) {
|
||||
processAudioData = true;
|
||||
transcoder.setProcessAudioData(processAudioData);
|
||||
this.scope = scope;
|
||||
this.transcoder = transcoder;
|
||||
rtpStreamReceiver = new RtpStreamReceiver(socket, transcoder.getIncomingEncodedFrameSize());
|
||||
rtpStreamReceiver.setRtpStreamReceiverListener(this);
|
||||
listenStreamName = "speaker_" + System.currentTimeMillis();
|
||||
scope.setName(listenStreamName);
|
||||
streamFromSip = new PipedOutputStream();
|
||||
try {
|
||||
streamToFlash = new PipedInputStream(streamFromSip);
|
||||
// streamFromSip = new PipedOutputStream();
|
||||
// try {
|
||||
// streamToFlash = new PipedInputStream(streamFromSip);
|
||||
startNow();
|
||||
mBuffer = IoBuffer.allocate(1024);
|
||||
mBuffer = mBuffer.setAutoExpand(true);
|
||||
audioData = new AudioData();
|
||||
} catch (IOException e) {
|
||||
// } catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
transcoder.setTranscodedAudioListener(this); //
|
||||
}
|
||||
|
||||
public String getStreamName() {
|
||||
@ -102,6 +96,7 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
||||
public void stop() {
|
||||
log.debug("Stopping stream for {}", listenStreamName);
|
||||
processAudioData = false;
|
||||
transcoder.setProcessAudioData(processAudioData);
|
||||
rtpStreamReceiver.stop();
|
||||
log.debug("Stopped RTP Stream Receiver for {}", listenStreamName);
|
||||
if (audioBroadcastStream != null) {
|
||||
@ -137,18 +132,19 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
||||
|
||||
audioBroadcastStream.start();
|
||||
processAudioData = true;
|
||||
transcoder.setProcessAudioData(processAudioData);
|
||||
|
||||
audioDataProcessor = new Runnable() {
|
||||
/* audioDataProcessor = new Runnable() {
|
||||
public void run() {
|
||||
processAudioData();
|
||||
}
|
||||
};
|
||||
exec.execute(audioDataProcessor);
|
||||
|
||||
*/
|
||||
rtpStreamReceiver.start();
|
||||
}
|
||||
|
||||
private void processAudioData() {
|
||||
/* private void processAudioData() {
|
||||
int len = 160;
|
||||
byte[] pcmAudio = new byte[len];
|
||||
int remaining = len;
|
||||
@ -171,7 +167,7 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@Override
|
||||
public void onStoppedReceiving() {
|
||||
if (observer != null) observer.onStreamStopped();
|
||||
@ -179,12 +175,7 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
||||
|
||||
@Override
|
||||
public void onAudioDataReceived(byte[] audioData, int offset, int len) {
|
||||
try {
|
||||
streamFromSip.write(audioData, offset, len);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
transcoder.handleData(audioData, offset, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,9 +19,13 @@
|
||||
**/
|
||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||
|
||||
public interface FlashToSipTranscoder {
|
||||
void transcode(byte[] audioData, int startOffset, int length, TranscodedAudioDataListener listener);
|
||||
import org.bigbluebutton.voiceconf.red5.media.FlashToSipAudioStream.TranscodedAudioListener;
|
||||
|
||||
public interface FlashToSipTranscoder {
|
||||
void transcode(byte[] audioData, int startOffset, int length);
|
||||
void handlePacket(byte[] data, int begin, int end);
|
||||
int getOutgoingEncodedFrameSize();
|
||||
int getCodecId();
|
||||
void setTranscodedAudioListener(TranscodedAudioListener transcodedAudioListener);
|
||||
void setProcessAudioData(boolean isProcessing);
|
||||
}
|
||||
|
@ -19,10 +19,16 @@
|
||||
**/
|
||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.bigbluebutton.voiceconf.red5.media.FlashToSipAudioStream.TranscodedAudioListener;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.red5.app.sip.codecs.Codec;
|
||||
import org.red5.app.sip.codecs.asao.Decoder;
|
||||
@ -65,6 +71,14 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
private long timestamp = 0;
|
||||
private final static int TS_INCREMENT = 180; // Determined from PCAP traces.
|
||||
|
||||
private final PipedOutputStream streamFromFlash;
|
||||
private PipedInputStream streamToSip;
|
||||
|
||||
private final Executor exec = Executors.newSingleThreadExecutor();
|
||||
private Runnable audioDataProcessor;
|
||||
private boolean processAudioData = false;
|
||||
private TranscodedAudioListener transcodedAudioListener;
|
||||
|
||||
/**
|
||||
* The transcode process works by taking a 64-byte-array Nelly audio and converting it into a 256-float-array L16 audio. From the
|
||||
* 256-float-array L16 audio, we take 160-float-array and convert it to a 160-byte-array Ulaw audio. The remaining 96-float-array
|
||||
@ -79,6 +93,22 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
Random rgen = new Random();
|
||||
timestamp = rgen.nextInt(1000);
|
||||
viewBuffer = l16Audio.asReadOnlyBuffer();
|
||||
|
||||
streamFromFlash = new PipedOutputStream();
|
||||
try {
|
||||
streamToSip = new PipedInputStream(streamFromFlash);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
processAudioData = true;
|
||||
audioDataProcessor = new Runnable() {
|
||||
public void run() {
|
||||
processAudioData();
|
||||
}
|
||||
};
|
||||
exec.execute(audioDataProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,8 +121,42 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
return sipCodec.getCodecId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePacket(byte[] data, int begin, int end) {
|
||||
try {
|
||||
streamFromFlash.write(data, begin, end);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void processAudioData() {
|
||||
int len = 64;
|
||||
byte[] nellyAudio = new byte[len];
|
||||
int remaining = len;
|
||||
int offset = 0;
|
||||
|
||||
while (processAudioData) {
|
||||
try {
|
||||
int bytesRead = streamToSip.read(nellyAudio, offset, remaining);
|
||||
remaining -= bytesRead;
|
||||
if (remaining == 0) {
|
||||
remaining = len;
|
||||
offset = 0;
|
||||
transcode(nellyAudio, 0, nellyAudio.length);
|
||||
} else {
|
||||
offset += bytesRead;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transcode(byte[] audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
|
||||
public void transcode(byte[] audioData, int startOffset, int length) {
|
||||
if (audioData.length != NELLY_AUDIO_LENGTH) {
|
||||
log.warn("Receiving bad nelly audio. Expecting {}, got {}.", NELLY_AUDIO_LENGTH, audioData.length);
|
||||
return;
|
||||
@ -111,7 +175,7 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
int encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||
|
||||
// Send it to the server
|
||||
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
transcodedAudioListener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
|
||||
if (l16Audio.position() == l16Audio.capacity()) {
|
||||
/**
|
||||
@ -125,7 +189,7 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
viewBuffer.get(tempUlawBuffer);
|
||||
encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||
if (encodedBytes == sipCodec.getOutgoingEncodedFrameSize()) {
|
||||
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
transcodedAudioListener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
} else {
|
||||
log.error("Failure encoding buffer." );
|
||||
}
|
||||
@ -134,7 +198,7 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
viewBuffer.get(tempUlawBuffer);
|
||||
encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||
if (encodedBytes == sipCodec.getOutgoingEncodedFrameSize()) {
|
||||
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
transcodedAudioListener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
} else {
|
||||
log.error("Failure encoding buffer." );
|
||||
}
|
||||
@ -143,7 +207,7 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
viewBuffer.get(tempUlawBuffer);
|
||||
encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||
if (encodedBytes == sipCodec.getOutgoingEncodedFrameSize()) {
|
||||
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
transcodedAudioListener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||
} else {
|
||||
log.error("Failure encoding buffer." );
|
||||
}
|
||||
@ -153,4 +217,13 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
viewBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTranscodedAudioListener(TranscodedAudioListener transcodedAudioListener) {
|
||||
this.transcodedAudioListener = transcodedAudioListener;
|
||||
}
|
||||
@Override
|
||||
public void setProcessAudioData(boolean isProcessing){
|
||||
processAudioData = isProcessing;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,9 +19,16 @@
|
||||
**/
|
||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.bigbluebutton.voiceconf.red5.media.SipToFlashAudioStream;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.red5.server.api.IConnection;
|
||||
import org.red5.server.api.Red5;
|
||||
@ -70,6 +77,16 @@ public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||
private long timestamp = 0;
|
||||
private final static int TS_INCREMENT = 32; // Determined from PCAP traces.
|
||||
|
||||
private final PipedOutputStream streamFromSip;
|
||||
private PipedInputStream streamToFlash;
|
||||
|
||||
// private NellySipToFlashTranscoderImp transcoder;
|
||||
|
||||
private TranscodedAudioDataListener transcodedAudioListener;
|
||||
private boolean processAudioData;
|
||||
|
||||
private final Executor exec = Executors.newSingleThreadExecutor();
|
||||
private Runnable audioDataProcessor;
|
||||
/**
|
||||
* The transcode takes a 160-byte Ulaw audio and converts it to a 160-float L16 audio. Whenever there is an
|
||||
* available 256-float L16 audio, that gets converted into a 64-byte Nelly audio. Therefore, 8 Ulaw packets
|
||||
@ -82,17 +99,24 @@ public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||
Random rgen = new Random();
|
||||
timestamp = rgen.nextInt(1000);
|
||||
viewBuffer = l16Audio.asReadOnlyBuffer();
|
||||
streamFromSip = new PipedOutputStream();
|
||||
try {
|
||||
streamToFlash = new PipedInputStream(streamFromSip);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
startNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transcode(byte[] audioData, TranscodedAudioDataListener listener) {
|
||||
public void transcode(byte[] audioData) {
|
||||
if (audioData.length != ULAW_AUDIO_LENGTH) {
|
||||
log.warn("Received corrupt audio. Got {}, expected {}.", audioData.length, ULAW_AUDIO_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert Ulaw to L16
|
||||
int decodedBytes = audioCodec.codecToPcm(audioData, tempL16Buffer);
|
||||
audioCodec.codecToPcm(audioData, tempL16Buffer);
|
||||
|
||||
// Store into the buffer
|
||||
l16Audio.put(tempL16Buffer);
|
||||
@ -118,7 +142,7 @@ public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||
}
|
||||
}
|
||||
|
||||
if (sendPacket) listener.handleTranscodedAudioData(nellyBytes, timestamp += TS_INCREMENT);
|
||||
if (sendPacket) transcodedAudioListener.handleTranscodedAudioData(nellyBytes, timestamp += TS_INCREMENT);
|
||||
}
|
||||
|
||||
if (l16Audio.position() == l16Audio.capacity()) {
|
||||
@ -137,5 +161,64 @@ public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||
public int getCodecId() {
|
||||
return NELLYMOSER_CODEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleData(byte[] audioData, int offset, int len) {
|
||||
try {
|
||||
streamFromSip.write(audioData, offset, len);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void startNow() {
|
||||
|
||||
audioDataProcessor = new Runnable() {
|
||||
public void run() {
|
||||
processAudioData();
|
||||
}
|
||||
};
|
||||
exec.execute(audioDataProcessor);
|
||||
|
||||
}
|
||||
|
||||
private void processAudioData() {
|
||||
int len = 160;
|
||||
byte[] pcmAudio = new byte[len];
|
||||
int remaining = len;
|
||||
int offset = 0;
|
||||
|
||||
while (processAudioData) {
|
||||
try {
|
||||
int bytesRead = streamToFlash.read(pcmAudio, offset, remaining);
|
||||
remaining -= bytesRead;
|
||||
if (remaining == 0) {
|
||||
remaining = len;
|
||||
offset = 0;
|
||||
transcode(pcmAudio);
|
||||
} else {
|
||||
offset += bytesRead;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProcessAudioData(boolean isProcessing){
|
||||
processAudioData = isProcessing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTranscodedAudioListener(
|
||||
SipToFlashAudioStream sipToFlashAudioStream) {
|
||||
this.transcodedAudioListener = sipToFlashAudioStream;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,14 @@
|
||||
**/
|
||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||
|
||||
import org.bigbluebutton.voiceconf.red5.media.SipToFlashAudioStream;
|
||||
|
||||
public interface SipToFlashTranscoder {
|
||||
void transcode(byte[] audioData, TranscodedAudioDataListener listener);
|
||||
void transcode(byte[] audioData);
|
||||
int getCodecId();
|
||||
int getIncomingEncodedFrameSize();
|
||||
void handleData(byte[] audioData, int offset, int len);
|
||||
void setTranscodedAudioListener(SipToFlashAudioStream sipToFlashAudioStream);
|
||||
void setProcessAudioData(boolean isProcessing);
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bigbluebutton.voiceconf.red5.media.FlashToSipAudioStream.TranscodedAudioListener;
|
||||
import org.red5.app.sip.codecs.Codec;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
@ -30,17 +32,19 @@ public class SpeexFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
private Codec audioCodec;
|
||||
private long timestamp = 0;
|
||||
private final static int TS_INCREMENT = 320; // Determined from PCAP traces.
|
||||
private TranscodedAudioListener transcodedAudioListener;
|
||||
|
||||
public SpeexFlashToSipTranscoderImp(Codec audioCodec) {
|
||||
this.audioCodec = audioCodec;
|
||||
Random rgen = new Random();
|
||||
timestamp = rgen.nextInt(1000);
|
||||
System.out.println("Speex start!");
|
||||
}
|
||||
|
||||
public void transcode(byte[] audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
|
||||
public void transcode(byte[] audioData, int startOffset, int length) {
|
||||
byte[] transcodedAudio = new byte[length];
|
||||
System.arraycopy(audioData, startOffset, transcodedAudio, 0, length);
|
||||
listener.handleTranscodedAudioData(transcodedAudio, timestamp += TS_INCREMENT);
|
||||
transcodedAudioListener.handleTranscodedAudioData(transcodedAudio, timestamp += TS_INCREMENT);
|
||||
}
|
||||
|
||||
public int getCodecId() {
|
||||
@ -54,4 +58,23 @@ public class SpeexFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||
public int getOutgoingPacketization() {
|
||||
return audioCodec.getOutgoingPacketization();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePacket(byte[] data, int begin, int end) {
|
||||
transcode(data, begin, end);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTranscodedAudioListener(
|
||||
TranscodedAudioListener transcodedAudioListener) {
|
||||
this.transcodedAudioListener = transcodedAudioListener;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProcessAudioData(boolean isProcessing) {
|
||||
// TODO do nothing here;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bigbluebutton.voiceconf.red5.media.SipToFlashAudioStream;
|
||||
import org.red5.app.sip.codecs.Codec;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
@ -31,7 +33,8 @@ public class SpeexSipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||
private Codec audioCodec = null;
|
||||
private long timestamp = 0;
|
||||
private static final int TS_INCREMENT = 20; // Determined from PCAP traces.
|
||||
|
||||
private TranscodedAudioDataListener transcodedAudioListener;
|
||||
|
||||
public SpeexSipToFlashTranscoderImp(Codec codec) {
|
||||
this.audioCodec = codec;
|
||||
Random rgen = new Random();
|
||||
@ -39,9 +42,10 @@ public class SpeexSipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transcode(byte[] audioData, TranscodedAudioDataListener listener) {
|
||||
public void transcode(byte[] audioData ) {
|
||||
byte[] codedBuffer = audioData;
|
||||
listener.handleTranscodedAudioData(codedBuffer, timestamp += TS_INCREMENT);
|
||||
// System.out.println("Speex transcode:"+audioData.length);
|
||||
transcodedAudioListener.handleTranscodedAudioData(codedBuffer, timestamp += TS_INCREMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -54,5 +58,25 @@ public class SpeexSipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||
return audioCodec.getIncomingEncodedFrameSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleData(byte[] audioData, int offset, int len) {
|
||||
byte[] data = new byte[len];
|
||||
System.arraycopy(audioData, offset, data, 0, len);
|
||||
transcode(data);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTranscodedAudioListener(
|
||||
SipToFlashAudioStream sipToFlashAudioStream) {
|
||||
this.transcodedAudioListener = sipToFlashAudioStream;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProcessAudioData(boolean isProcessing) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user