Commit 2f20b26c authored by Dele Olajide's avatar Dele Olajide Committed by dele

Rayo plugin - Implemented OPUS decoding/encoding for SIP calls

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13802 b35dd754-fafc-0310-a699-88a17e54d16e
parent de9cab2e
......@@ -124,17 +124,15 @@ if (false) {
(byte)125, RtpPacket.SPEEX_ENCODING, 32000, 2, false));
}
public MediaInfo(byte payload , int encoding, int sampleRate,
int channels, boolean isTelephoneEventPayload) {
this.payload = payload;
this.encoding = encoding;
this.sampleRate = sampleRate;
this.channels = channels;
this.isTelephoneEventPayload = isTelephoneEventPayload;
samplesPerPacket =
sampleRate * channels / (1000 / RtpPacket.PACKET_PERIOD);
public MediaInfo(byte payload , int encoding, int sampleRate, int channels, boolean isTelephoneEventPayload)
{
this.payload = payload;
this.encoding = encoding;
this.sampleRate = sampleRate;
this.channels = channels;
this.isTelephoneEventPayload = isTelephoneEventPayload;
samplesPerPacket = sampleRate * channels / (1000 / RtpPacket.PACKET_PERIOD);
}
public static MediaInfo findMediaInfo(int encoding, int sampleRate,
......
......@@ -3,12 +3,12 @@
*
* This file is part of jVoiceBridge.
*
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* to you.
*
* jVoiceBridge is distributed in the hope that it will be useful,
* jVoiceBridge is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
......@@ -17,8 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied this
* code.
* exception as provided by Sun in the License file that accompanied this
* code.
*/
package com.sun.voip;
......@@ -49,7 +49,7 @@ public class SdpInfo {
private MediaInfo transmitMediaInfo;
private boolean transmitMediaInfoOk;
public SdpInfo(String remoteHost, int remotePort,
public SdpInfo(String remoteHost, int remotePort,
byte telephoneEventPayload, Vector supportedMedia,
MediaInfo mediaInfo, boolean preferredMediaSpecified) {
......@@ -136,7 +136,7 @@ public class SdpInfo {
public MediaInfo getMediaInfo() {
return mediaInfo;
}
public void setTransmitMediaInfoOk(boolean transmitMediaInfoOk) {
this.transmitMediaInfoOk = transmitMediaInfoOk;
}
......@@ -150,7 +150,7 @@ public class SdpInfo {
}
public MediaInfo getTransmitMediaInfo() {
if (transmitMediaInfo == null || mediaInfo.getPayload() ==
if (transmitMediaInfo == null || mediaInfo.getPayload() ==
RtpPacket.PCMU_PAYLOAD) {
return mediaInfo;
......@@ -176,7 +176,7 @@ public class SdpInfo {
Logger.println(e.getMessage());
Logger.println("Using transmit media info " + transmitMediaInfo);
}
return transmitMediaInfo;
}
......@@ -204,7 +204,7 @@ public class SdpInfo {
return true;
}
public MediaInfo getMediaInfo(int sampleRate, int channels, int encoding)
public MediaInfo getMediaInfo(int sampleRate, int channels, int encoding)
throws IOException {
if (supportedMedia != null) {
......@@ -224,8 +224,7 @@ public class SdpInfo {
+ encoding + "/" + sampleRate + "/" + channels);
}
public MediaInfo findBestMediaInfo(Vector otherSupportedMedia,
MediaInfo otherMediaPreference) throws IOException {
public MediaInfo findBestMediaInfo(Vector otherSupportedMedia, MediaInfo otherMediaPreference) throws IOException {
MediaInfo best = null;
......@@ -239,7 +238,7 @@ public class SdpInfo {
if (otherMediaPreference != null) {
if (m.getSampleRate() > otherMediaPreference.getSampleRate() ||
m.getChannels() > otherMediaPreference.getChannels()) {
continue;
}
}
......@@ -257,7 +256,7 @@ public class SdpInfo {
}
private boolean isBetter(MediaInfo m1, MediaInfo m2) {
if (m1.getSampleRate() > m2.getSampleRate() &&
if (m1.getSampleRate() > m2.getSampleRate() &&
m1.getChannels() >= m2.getChannels()) {
return true;
......
......@@ -3,12 +3,12 @@
*
* This file is part of jVoiceBridge.
*
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* to you.
*
* jVoiceBridge is distributed in the hope that it will be useful,
* jVoiceBridge is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
......@@ -17,8 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied this
* code.
* exception as provided by Sun in the License file that accompanied this
* code.
*/
package com.sun.voip;
......@@ -74,7 +74,7 @@ public class SdpManager {
SdpManager.supportedMedia = supportedMedia;
}
public void setPreferredMedia(int encoding, int sampleRate, int channels)
public void setPreferredMedia(int encoding, int sampleRate, int channels)
throws ParseException {
if (sampleRate == 8000 && channels == 1) {
......@@ -95,7 +95,7 @@ public class SdpManager {
return localMediaPreference;
}
public void setTransmitMediaInfo(int encoding, int sampleRate,
public void setTransmitMediaInfo(int encoding, int sampleRate,
int channels) throws ParseException {
transmitMediaInfo = findMediaInfo(encoding, sampleRate, channels);
......@@ -124,16 +124,13 @@ public class SdpManager {
for (int i = 0; i < supportedMedia.size(); i++) {
MediaInfo mediaInfo = (MediaInfo) supportedMedia.elementAt(i);
if (mediaInfo.getSampleRate() > maxSampleRate ||
mediaInfo.getChannels() > maxChannels) {
continue;
if (mediaInfo.getSampleRate() > maxSampleRate || mediaInfo.getChannels() > maxChannels) {
continue;
}
if (useTelephoneEvent == false &&
mediaInfo.isTelephoneEventPayload()) {
continue;
if (useTelephoneEvent == false && mediaInfo.isTelephoneEventPayload())
{
continue;
}
if (n > 0) {
......@@ -153,14 +150,13 @@ public class SdpManager {
private String getRtpmaps() {
String rtpmaps = "";
for (int i = 0; i < supportedMedia.size(); i++) {
MediaInfo mediaInfo = (MediaInfo)
supportedMedia.elementAt(i);
if (mediaInfo.getSampleRate() > maxSampleRate ||
mediaInfo.getChannels() > maxChannels) {
for (int i = 0; i < supportedMedia.size(); i++)
{
MediaInfo mediaInfo = (MediaInfo) supportedMedia.elementAt(i);
continue;
if (mediaInfo.getSampleRate() > maxSampleRate || mediaInfo.getChannels() > maxChannels)
{
continue;
}
rtpmaps += generateRtpmap(mediaInfo) + "\r\n";
......@@ -181,7 +177,7 @@ public class SdpManager {
*/
private String generateRtpmap(MediaInfo mediaInfo) {
if (mediaInfo.isTelephoneEventPayload()) {
// if (useTelephoneEvent == false) {
// if (useTelephoneEvent == false) {
// return "";
// }
......@@ -189,7 +185,7 @@ public class SdpManager {
+ " telephone-event/8000";
}
return "a=rtpmap:" + mediaInfo.getPayload() + " "
return "a=rtpmap:" + mediaInfo.getPayload() + " "
+ mediaInfo.getEncodingString() + "/"
+ mediaInfo.getSampleRate() + "/"
+ mediaInfo.getChannels();
......@@ -198,7 +194,7 @@ public class SdpManager {
/*
* Find the MediaInfo for a specified payload
*/
public static MediaInfo findMediaInfo(byte payload)
public static MediaInfo findMediaInfo(byte payload)
throws ParseException {
for (int i = 0; i < supportedMedia.size(); i++) {
......@@ -235,8 +231,8 @@ public class SdpManager {
}
}
throw new ParseException("Unsupported media " +
"encoding " + encoding + " sample rate " + sampleRate
throw new ParseException("Unsupported media " +
"encoding " + encoding + " sample rate " + sampleRate
+ " channels " + channels, 0);
}
......@@ -246,7 +242,7 @@ public class SdpManager {
+ "o=" + name + " 1 1 IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "s=SIP Call\r\n"
+ "c=IN IP4 "
+ "c=IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "t=0 0 \r\n"
+ "m=audio " + isa.getPort()
......@@ -255,112 +251,105 @@ public class SdpManager {
+ getRtpmaps();
if (localMediaPreference != null) {
sdp += "a=PreferredPayload:"
sdp += "a=PreferredPayload:"
+ localMediaPreference.getPayload() + "\r\n";
}
if (transmitMediaInfo != null) {
sdp += "a=transmitPayload:"
sdp += "a=transmitPayload:"
+ transmitMediaInfo.getPayload() + "\r\n";
}
return sdp;
}
public String generateSdp(String name, InetSocketAddress isa,
SdpInfo remoteSdpInfo) throws IOException {
public String generateSdp(String name, InetSocketAddress isa, SdpInfo remoteSdpInfo) throws IOException
{
MediaInfo mediaInfo = null;
MediaInfo mediaInfo = null;
if (localMediaPreference != null) {
if (remoteSdpInfo.isSupported(localMediaPreference)) {
mediaInfo = localMediaPreference;
if (Logger.logLevel >= Logger.LOG_INFO) {
Logger.println("Using local media preference: " + mediaInfo);
if (localMediaPreference != null)
{
if (remoteSdpInfo.isSupported(localMediaPreference)) {
mediaInfo = localMediaPreference;
Logger.println("Using local media preference: " + mediaInfo);
}
}
}
}
/*
* Try remote media preference
*/
if (mediaInfo == null && remoteSdpInfo.preferredMediaSpecified()) {
MediaInfo remoteMediaPreference = remoteSdpInfo.getMediaInfo();
if (remoteMediaPreference.getSampleRate() <= maxSampleRate &&
remoteMediaPreference.getChannels() <= maxChannels) {
/*
* See if remote media preference is supported
*/
try {
mediaInfo =
findMediaInfo(remoteMediaPreference.getPayload());
Logger.println("Using remote media preference: "
+ mediaInfo);
} catch (ParseException e) {
}
}
/*
* Try remote media preference
*/
if (remoteSdpInfo.preferredMediaSpecified())
{
MediaInfo remoteMediaPreference = remoteSdpInfo.getMediaInfo();
if (remoteMediaPreference.getSampleRate() <= maxSampleRate &&
remoteMediaPreference.getChannels() <= maxChannels) {
/*
* See if remote media preference is supported
*/
try {
mediaInfo = findMediaInfo(remoteMediaPreference.getPayload());
Logger.println("Using remote media preference: " + mediaInfo);
} catch (ParseException e) {
}
}
}
if (mediaInfo == null) {
/*
* default to 8000/1 ulaw
*/
mediaInfo = remoteSdpInfo.findBestMediaInfo(supportedMedia,
localMediaPreference);
if (mediaInfo == null) {
/*
* default to 8000/1 ulaw
*/
mediaInfo = remoteSdpInfo.findBestMediaInfo(supportedMedia, localMediaPreference);
Logger.println("Using best media " + mediaInfo);
}
Logger.println("Using best media " + mediaInfo);
}
remoteSdpInfo.setMediaInfo(mediaInfo);
remoteSdpInfo.setMediaInfo(mediaInfo);
String payloads = "13 " + mediaInfo.getPayload();
String payloads = "13 " + mediaInfo.getPayload();
byte telephoneEventPayload = remoteSdpInfo.getTelephoneEventPayload();
byte telephoneEventPayload = remoteSdpInfo.getTelephoneEventPayload();
String telephoneEvent = "";
String telephoneEvent = "";
if (useTelephoneEvent == true && telephoneEventPayload != 0) {
try {
MediaInfo m = findMediaInfo(telephoneEventPayload);
payloads += " " + telephoneEventPayload;
telephoneEvent += generateRtpmap(m) + "\r\n";
} catch (ParseException e) {
Logger.println("Failed to add rtpmap for telephone event "
+ telephoneEventPayload);
}
}
if (useTelephoneEvent == true && telephoneEventPayload != 0) {
try {
MediaInfo m = findMediaInfo(telephoneEventPayload);
payloads += " " + telephoneEventPayload;
telephoneEvent += generateRtpmap(m) + "\r\n";
} catch (ParseException e) {
Logger.println("Failed to add rtpmap for telephone event " + telephoneEventPayload);
}
}
String transmitMap = "";
String transmitMap = "";
if (transmitMediaInfo != null) {
transmitMap = generateRtpmap(transmitMediaInfo) + "\r\n";
}
if (transmitMediaInfo != null) {
transmitMap = generateRtpmap(transmitMediaInfo) + "\r\n";
}
String sdp =
"v=0\r\n"
+ "o=" + name + " 1 1 IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "s=SIP Call\r\n"
+ "c=IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "t=0 0 \r\n"
+ "m=audio " + isa.getPort()
+ " RTP/AVP " + payloads + "\r\n"
+ "a=rtpmap:13 CN/8000" + "\r\n"
+ generateRtpmap(mediaInfo) + "\r\n"
+ transmitMap
+ telephoneEvent;
String sdp =
"v=0\r\n"
+ "o=" + name + " 1 1 IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "s=SIP Call\r\n"
+ "c=IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "t=0 0 \r\n"
+ "m=audio " + isa.getPort()
+ " RTP/AVP " + payloads + "\r\n"
+ "a=rtpmap:13 CN/8000" + "\r\n"
+ generateRtpmap(mediaInfo) + "\r\n"
+ transmitMap
+ telephoneEvent;
if (transmitMediaInfo != null) {
sdp += "a=transmitPayload:"
+ transmitMediaInfo.getPayload() + "\r\n";
}
return sdp;
return sdp;
}
}
......@@ -172,7 +172,7 @@ public class SdpParser {
+ payloads[i], 0);
}
if (payload != 0 && payload < 96 || payload > 127) {
if (payload != 0 && (payload < 96 || payload > 127)) {
/*
* Not one we can deal with
*/
......
......@@ -323,10 +323,9 @@ public abstract class CallHandler extends Thread {
* Send indication when a dtmf key is pressed
*/
public void dtmfKeys(String dtmfKeys) {
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println(cp + " got dtmf keys " + dtmfKeys + " "
+ cp.dtmfDetection());
}
//if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println(cp + " got dtmf keys " + dtmfKeys + " " + cp.dtmfDetection());
//}
if (isCallEstablished()) {
if (cp.dtmfDetection()) {
......
......@@ -97,46 +97,49 @@ public class IncomingCallHandler extends CallHandler
addCallEventListener(this);
if (cp.getConferenceId() == null || cp.getConferenceId().length() == 0)
if (directConferencing)
{
if (directConferencing)
if (cp.getConferenceId() == null || cp.getConferenceId().length() == 0)
{
System.out.println("Don't have conf, using default....");
cp.setConferenceId(defaultIncomingConferenceId); // wait in lobby
} else {
System.out.println("Incoming SIP, call " + cp);
Logger.println("Have conf " + cp.getConferenceId());
haveIncomingConferenceId = true; // goto your conference
}
if (RayoComponent.self.routeIncomingSIP(cp))
{
haveIncomingConferenceId = true;
start();
} else {
// conf bridge
} else {
if (Config.getInstance().getConferenceExten().equals(cp.getToPhoneNumber()))
{
incomingConferenceHandler = new IncomingConferenceHandler(this, cp.getToPhoneNumber());
System.out.println("Incoming SIP, call " + cp);
} else if (Config.getInstance().getConferenceByPhone(cp.getToPhoneNumber()) != null) {
if (RayoComponent.self.routeIncomingSIP(cp))
{
haveIncomingConferenceId = true;
start();
incomingConferenceHandler = new IncomingConferenceHandler(this, cp.getToPhoneNumber());
} else {
// conf bridge
} else {
cancelRequest(cp.getToPhoneNumber() + " is not a valid endpoint"); // reject call
}
}
if (Config.getInstance().getConferenceExten().equals(cp.getToPhoneNumber()))
{
incomingConferenceHandler = new IncomingConferenceHandler(this, cp.getToPhoneNumber());
start();
}
} else if (Config.getInstance().getConferenceByPhone(cp.getToPhoneNumber()) != null) {
} else {
incomingConferenceHandler = new IncomingConferenceHandler(this, cp.getToPhoneNumber());
start();
Logger.println("Have conf " + cp.getConferenceId());
haveIncomingConferenceId = true; // goto your conference
}
} else {
cancelRequest(cp.getToPhoneNumber() + " is not a valid endpoint"); // reject call
}
}
start();
}
}
public static void setDirectConferencing(boolean directConferencing) {
......@@ -350,6 +353,9 @@ public class IncomingCallHandler extends CallHandler
public void callEventNotification(CallEvent callEvent) {
Logger.println("IncomingCallHandler " + callEvent.toString());
if (callEvent.equals(callEvent.STATE_CHANGED) &&
callEvent.getCallState().equals(CallState.ESTABLISHED)) {
......@@ -461,6 +467,8 @@ public class IncomingCallHandler extends CallHandler
public ConferenceManager transferCall(String conferenceId)
throws IOException {
System.out.println("transferCall " + conferenceId);
ConferenceManager conferenceManager = transferCall(this, conferenceId);
String s = getNumberOfCallsAsTreatment(conferenceManager.getNumberOfMembers());
......
......@@ -80,10 +80,12 @@ public class IncomingConferenceHandler extends Thread
* Constructor.
*/
public IncomingConferenceHandler(IncomingCallHandler incomingCallHandler, String phoneNo) {
this.incomingCallHandler = incomingCallHandler;
this.phoneNo = phoneNo;
this.incomingCallHandler = incomingCallHandler;
this.phoneNo = phoneNo;
incomingCallHandler.addCallEventListener(this);
incomingCallHandler.addCallEventListener(this);
Logger.println("IncomingConferenceHandler: " + phoneNo);
}
private String lastMessagePlayed;
......@@ -121,9 +123,9 @@ public class IncomingConferenceHandler extends Thread
* Called when status for an incoming call changes.
*/
public void callEventNotification(CallEvent callEvent) {
if (Logger.logLevel >= Logger.LOG_INFO) {
Logger.println(callEvent.toString());
}
//if (Logger.logLevel >= Logger.LOG_INFO) {
Logger.println("IncomingConferenceHandler " + callEvent.toString());
//}
if (callEvent.equals(CallEvent.STATE_CHANGED) &&
callEvent.getCallState().equals(CallState.ESTABLISHED)) {
......@@ -132,18 +134,19 @@ public class IncomingConferenceHandler extends Thread
* New incoming call
*/
if (callEvent.getInfo() != null) {
Logger.println("IncomingConferenceHandler: "
+ callEvent.getInfo());
Logger.println("IncomingConferenceHandler: " + callEvent.getInfo());
}
if (Config.getInstance().getMeetingCode(phoneNo) != null)
{
meetingCode = Config.getInstance().getMeetingCode(phoneNo);
Logger.println("IncomingConferenceHandler: meeting code " + meetingCode);
if (Config.getInstance().getPassCode(meetingCode, phoneNo) == null)
{
try {
incomingCallHandler.transferCall(Config.getInstance().getMeetingCode(phoneNo));
incomingCallHandler.transferCall(meetingCode);
state = IN_MEETING;
} catch (IOException e) {
......
......@@ -71,6 +71,7 @@ import java.awt.Point;
import org.ifsoft.*;
import org.ifsoft.rtp.*;
import org.jitsi.impl.neomedia.codec.audio.opus.Opus;
/**
* Receive RTP data for this ConferenceMember, add it to the mix
......@@ -116,6 +117,15 @@ public class MemberReceiver implements MixDataSource, TreatmentDoneListener {
private SpeexDecoder speexDecoder;
private long opusDecoder = 0;
private final int opusSampleRate = 48000;
private final int frameSizeInMillis = 20;
private final int outputFrameSize = 2;
private final int opusChannels = 2;
private int frameSizeInSamplesPerChannel = (opusSampleRate * frameSizeInMillis) / 1000;
private int frameSizeInBytes = outputFrameSize * opusChannels * frameSizeInSamplesPerChannel;
private int dropPackets;
private boolean done = false;
......@@ -448,11 +458,28 @@ public class MemberReceiver implements MixDataSource, TreatmentDoneListener {
Logger.println("Call " + cp + " created SpeexDecoder");
} catch (SpeexException e) {
Logger.println("Call " + cp + e.getMessage());
callHandler.cancelRequest(e.getMessage());
callHandler.cancelRequest(e.getMessage());
return;
}
} else if (myMediaInfo.getEncoding() == RtpPacket.PCM_ENCODING) {
try {
opusDecoder = Opus.decoder_create(opusSampleRate, opusChannels);
if (opusDecoder == 0)
{
Logger.println("Call " + cp + " OPUS decoder creation error ");
callHandler.cancelRequest("OPUS decoder creation error ");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (cp.getJoinConfirmationTimeout() == 0) {
joinConfirmationReceived = true;
readyToReceiveData = true;
......@@ -1170,9 +1197,7 @@ public class MemberReceiver implements MixDataSource, TreatmentDoneListener {
callHandler.getMember().adjustVolume(data, inputVolume);
}
//Logger.println("Call " + cp
// + " receiveMedia length " + length + " decoded int length "
// + data.length);
//Logger.println("Call " + cp + " receiveMedia length " + length + " decoded int length " + data.length);
int numberOfSamples = data.length;
......@@ -1246,60 +1271,77 @@ public class MemberReceiver implements MixDataSource, TreatmentDoneListener {
return numberOfSamples;
}
private int[] decodeToLinear(byte[] receivedData, int length)
throws SpeexException {
private int[] decodeToLinear(byte[] receivedData, int length) throws SpeexException
{
/*
* receivedData has the 12 byte RTP header.
*/
/*
* receivedData has the 12 byte RTP header.
*/
int[] data = new int[myMediaInfo.getSamplesPerPacket()];
int[] data = new int[myMediaInfo.getSamplesPerPacket()];
long start = 0;
long start = 0;
if (myMediaInfo.getEncoding() == RtpPacket.PCMU_ENCODING) {
if (traceCall || Logger.logLevel == -1) {
start = System.nanoTime();
}
if (myMediaInfo.getEncoding() == RtpPacket.PCMU_ENCODING)
{
if (traceCall || Logger.logLevel == -1)
{
start = System.nanoTime();
}
/*
* Convert ulaw data to linear. length is the ulaw
* data length plus the RTP header length.
*
* If the incoming packet is shorter, than we expect,
* the rest of <data> will be filled with 0 * which is PCM_SILENCE.
* data length plus the RTP header length.
*
* If the incoming packet is shorter, than we expect,
* the rest of <data> will be filled with 0 * which is PCM_SILENCE.
*/
AudioConversion.ulawToLinear(receivedData, RtpPacket.HEADER_SIZE,
length - RtpPacket.HEADER_SIZE, data);
if (length < 172 && Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("Call " + cp + " received short packet "
+ length);
}
AudioConversion.ulawToLinear(receivedData, RtpPacket.HEADER_SIZE, length - RtpPacket.HEADER_SIZE, data);
if (length < 172 && Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("Call " + cp + " received short packet " + length);
}
if (traceCall || Logger.logLevel == -1) {
Logger.println("Call " + cp + " ulawToLinear time "
+ ((System.nanoTime() - start) / 1000000000.)
+ " seconds");
Logger.println("Call " + cp + " ulawToLinear time " + ((System.nanoTime() - start) / 1000000000.) + " seconds");
}
} else if (myMediaInfo.getEncoding() == RtpPacket.PCM_ENCODING) {
int inputOffset = RtpPacket.HEADER_SIZE;
int inputLength = length - RtpPacket.HEADER_SIZE;
int frameSizeInSamplesPerChannel = Opus.decoder_get_nb_samples(opusDecoder, receivedData, inputOffset, inputLength);
if (frameSizeInSamplesPerChannel > 1)
{
int frameSizeInBytes = outputFrameSize * opusChannels * frameSizeInSamplesPerChannel;
byte[] output = new byte[frameSizeInBytes];
frameSizeInSamplesPerChannel = Opus.decode(opusDecoder, receivedData, inputOffset, inputLength, output, 0, frameSizeInSamplesPerChannel, 0);
data = AudioConversion.bytesToLittleEndianInts(output);
}
} else if (myMediaInfo.getEncoding() == RtpPacket.SPEEX_ENCODING) {
if (traceCall || Logger.logLevel == -1) {
start = System.nanoTime();
}
data = speexDecoder.decodeToIntArray(receivedData,
RtpPacket.HEADER_SIZE, length - RtpPacket.HEADER_SIZE);
data = speexDecoder.decodeToIntArray(receivedData, RtpPacket.HEADER_SIZE, length - RtpPacket.HEADER_SIZE);
if (traceCall || Logger.logLevel == -1) {
Logger.println("Call " + cp + " speex decode time "
+ ((System.nanoTime() - start) / 1000000000.)
+ " seconds");
}
} else {
AudioConversion.bytesToInts(receivedData, RtpPacket.HEADER_SIZE,
length - RtpPacket.HEADER_SIZE, data);
}
if (traceCall || Logger.logLevel == -1)
{
Logger.println("Call " + cp + " speex decode time " + ((System.nanoTime() - start) / 1000000000.) + " seconds");
}
return data;
} else {
AudioConversion.bytesToInts(receivedData, RtpPacket.HEADER_SIZE,
length - RtpPacket.HEADER_SIZE, data);
}
return data;
}
public synchronized void handleVP8Video(RTPPacket videoPacket)
......@@ -1827,56 +1869,62 @@ public class MemberReceiver implements MixDataSource, TreatmentDoneListener {
public void end() {
if (done) {
return;
}
}
done = true;
if (speechDetector != null && speechDetector.isSpeaking()) {
callHandler.speakingChanged(false);
}
if (speechDetector != null && speechDetector.isSpeaking()) {
callHandler.speakingChanged(false);
}
synchronized (recordingLock) {
if (recorder != null) {
recorder.done();
recorder = null;
}
}
synchronized (recordingLock) {
if (recorder != null) {
recorder.done();
recorder = null;
}
}
readyToReceiveData = false;
readyToReceiveData = false;
if (datagramChannelRegistered && datagramChannel != null) {
try {
datagramChannel.close();
if (datagramChannelRegistered && datagramChannel != null) {
try {
datagramChannel.close();
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("Call " + cp + " closed datagramChannel "
+ datagramChannel);
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("Call " + cp + " closed datagramChannel "
+ datagramChannel);
}
datagramChannel = null;
} catch (IOException e) {
Logger.println("Call " + cp
+ " exception closing datagram channel " + e.getMessage());
}
} else {
Logger.println("Call " + cp + " not closing datagramChannel");
}
datagramChannel = null;
} catch (IOException e) {
Logger.println("Call " + cp
+ " exception closing datagram channel " + e.getMessage());
}
} else {
Logger.println("Call " + cp + " not closing datagramChannel");
}
if (joinConfirmationReceived == true) {
String leaveTreatment;
if (joinConfirmationReceived == true) {
String leaveTreatment;
/**
* Play audio treatment to all conference members indicating that
* a member has left the conference.
*/
if ((leaveTreatment = cp.getConferenceLeaveTreatment()) != null) {
try {
conferenceManager.addTreatment(leaveTreatment);
} catch (IOException e) {
Logger.println("Call " + cp
+ " failed to start leave treatment " + leaveTreatment);
/**
* Play audio treatment to all conference members indicating that
* a member has left the conference.
*/
if ((leaveTreatment = cp.getConferenceLeaveTreatment()) != null) {
try {
conferenceManager.addTreatment(leaveTreatment);
} catch (IOException e) {
Logger.println("Call " + cp
+ " failed to start leave treatment " + leaveTreatment);
}
}
}
}
}
if (opusDecoder != 0)
{
Opus.decoder_destroy(opusDecoder);
opusDecoder = 0;
}
}
public void printStatistics() {
......
......@@ -55,6 +55,8 @@ import org.xmpp.jnodes.RelayChannel;
import org.ifsoft.*;
import org.ifsoft.rtp.*;
import org.jitsi.impl.neomedia.codec.audio.opus.Opus;
/**
* Send RTP data to this ConferenceMember,
*/
......@@ -73,6 +75,14 @@ public class MemberSender {
private RtpSenderPacket senderPacket;
private SpeexEncoder speexEncoder;
private long opusEncoder = 0;
private final int opusSampleRate = 48000;
private final int frameSizeInMillis = 20;
private final int outputFrameSize = 2;
private final int opusChannels = 2;
private int frameSizeInSamplesPerChannel = (opusSampleRate * frameSizeInMillis) / 1000;
private int frameSizeInBytes = outputFrameSize * opusChannels * frameSizeInSamplesPerChannel;
private InetSocketAddress memberAddress;
private boolean done = false;
......@@ -265,6 +275,23 @@ public class MemberSender {
}
}
if (myMediaInfo.getEncoding() == RtpPacket.PCM_ENCODING) {
try {
opusEncoder = Opus.encoder_create(opusSampleRate, opusChannels);
if (opusEncoder == 0)
{
Logger.println("Call " + cp + " OPUS encoder creation error ");
callHandler.cancelRequest("OPUS encoder creation error ");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
initializationDone = true;
......@@ -411,6 +438,8 @@ public class MemberSender {
//Logger.println("Call " + cp + " Sending data...");
byte[] opusBytes = null;
if (myMediaInfo.getEncoding() == RtpPacket.PCMU_ENCODING) {
/*
* Convert to ulaw
......@@ -433,13 +462,27 @@ public class MemberSender {
Logger.println("Call " + this + ": " + e.getMessage());
return false;
}
} else if (myMediaInfo.getEncoding() == RtpPacket.PCM_ENCODING) {
byte[] input = AudioConversion.littleEndianIntsToBytes(dataToSend);
byte[] output = new byte[Opus.MAX_PACKET];
int outLength = Opus.encode(opusEncoder, input, 0, frameSizeInSamplesPerChannel, output, 0, output.length);
opusBytes = new byte[outLength];
System.arraycopy(output, 0, opusBytes, 0, outLength);
System.arraycopy(output, 0, rtpData, RtpPacket.HEADER_SIZE, outLength);
senderPacket.setLength(outLength + RtpPacket.HEADER_SIZE);
//Logger.println("RtpPacket.PCM_ENCODING " + outLength);
} else {
AudioConversion.intsToBytes(dataToSend, rtpData, RtpPacket.HEADER_SIZE);
}
recordPacket(rtpData, senderPacket.getLength());
recordAudio(rtpData, RtpPacket.HEADER_SIZE,
senderPacket.getLength() - RtpPacket.HEADER_SIZE);
recordAudio(rtpData, RtpPacket.HEADER_SIZE, senderPacket.getLength() - RtpPacket.HEADER_SIZE);
/*
* Encrypt data if required
......@@ -469,14 +512,12 @@ public class MemberSender {
try {
senderPacket.setSocketAddress(memberAddress);
datagramChannel.send(
ByteBuffer.wrap(senderPacket.getData(), 0,
senderPacket.getLength()), memberAddress);
datagramChannel.send(ByteBuffer.wrap(senderPacket.getData(), 0, senderPacket.getLength()), memberAddress);
if (Logger.logLevel >= Logger.LOG_MOREDETAIL) {
Logger.writeFile("Call " + cp + " back from sending data");
}
} catch (IOException e) {
} catch (Exception e) {
if (!done) {
Logger.error("Call " + cp + " sendData " + e.getMessage());
e.printStackTrace();
......@@ -488,7 +529,8 @@ public class MemberSender {
} else {
try {
getWebRTCParticipant().pushAudio(rtpData, dataToSend);
getWebRTCParticipant().pushAudio(senderPacket.getData(), opusBytes);
} catch (Exception e) {
......@@ -758,8 +800,7 @@ public class MemberSender {
senderPacket.setSocketAddress(memberAddress);
try {
datagramChannel.send(
ByteBuffer.wrap(senderPacket.getData()), memberAddress);
datagramChannel.send(ByteBuffer.wrap(senderPacket.getData()), memberAddress);
} catch (IOException e) {
if (!done) {
Logger.println("Call " + cp + " sendComfortNoisePayload "
......@@ -854,16 +895,22 @@ public class MemberSender {
public void end() {
if (done) {
return;
}
}
done = true;
synchronized (recordingLock) {
if (recorder != null) {
recorder.done();
recorder = null;
}
}
synchronized (recordingLock) {
if (recorder != null) {
recorder.done();
recorder = null;
}
}
if (opusEncoder != 0)
{
Opus.encoder_destroy(opusEncoder);
opusEncoder = 0;
}
}
public void printStatistics() {
......
......@@ -3,12 +3,12 @@
*
* This file is part of jVoiceBridge.
*
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* to you.
*
* jVoiceBridge is distributed in the hope that it will be useful,
* jVoiceBridge is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
......@@ -17,8 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied this
* code.
* exception as provided by Sun in the License file that accompanied this
* code.
*/
package com.sun.voip.server;
......@@ -46,7 +46,7 @@ public class MixManager {
private SpatialAudio sa;
public MixManager(ConferenceMember member,
public MixManager(ConferenceMember member,
int conferenceSamplesPerPacket, int channels) {
this.member = member;
......@@ -66,7 +66,7 @@ public class MixManager {
}
sa.initialize(member.getConferenceManager().getId(),
member.getCallParticipant().getCallId(), sampleRate,
member.getCallParticipant().getCallId(), sampleRate,
channels, conferenceSamplesPerPacket / channels);
}
......@@ -119,7 +119,7 @@ public class MixManager {
mixDescriptors.add(mixDescriptor);
setUseFastMix();
}
public void addMix(MixDataSource mixDataSource, double attenuation) {
MixDescriptor mixDescriptor = findMixDescriptor(mixDataSource);
......@@ -127,7 +127,7 @@ public class MixManager {
if (mixDescriptor != null) {
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println("Call " + member
+ " Remove mix, volume 0 " + " mixDataSource "
+ " Remove mix, volume 0 " + " mixDataSource "
+ mixDataSource);
}
removeMix(mixDescriptor);
......@@ -147,7 +147,7 @@ public class MixManager {
mixDescriptors.add(mixDescriptor);
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println("created new mix for " + mixDataSource
Logger.println("created new mix for " + mixDataSource
+ " " + attenuation);
}
......@@ -158,7 +158,7 @@ public class MixManager {
mixDescriptor.setAttenuation(attenuation);
setUseFastMix();
}
public void removeMix(MixDataSource mixDataSource) {
MixDescriptor mixDescriptor = findMixDescriptor(mixDataSource);
......@@ -227,7 +227,7 @@ public class MixManager {
}
return;
}
for (int i = 0; i < 2; i++) {
MixDescriptor mixDescriptor = (MixDescriptor)
mixDescriptors.get(i);
......@@ -322,7 +322,7 @@ public class MixManager {
return forcePrivateMix;
}
public MixDescriptor setPrivateMix(MixDataSource mixDataSource,
public MixDescriptor setPrivateMix(MixDataSource mixDataSource,
double[] spatialValues) {
MixDescriptor mixDescriptor;
......@@ -334,7 +334,7 @@ public class MixManager {
}
if (mixDescriptor == null) {
mixDescriptor = new MixDescriptor(mixDataSource, 1.0,
mixDescriptor = new MixDescriptor(mixDataSource, 1.0,
spatialValues);
mixDescriptors.add(mixDescriptor);
......@@ -351,7 +351,7 @@ public class MixManager {
}
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println("MixManager: Setting private mix "
Logger.println("MixManager: Setting private mix "
+ mixDescriptor);
}
......@@ -389,7 +389,7 @@ public class MixManager {
outData = new int[conferenceSamplesPerPacket];
//Logger.println("Call " + member + " MixManager mixing "
//Logger.println("Call " + member + " MixManager mixing "
// + mixDescriptors.size());
boolean needToSend = false;
......@@ -428,7 +428,7 @@ public class MixManager {
spatialValues = sv;
if (Logger.logLevel == -69) {
Logger.println("Call " + member + " pm for "
Logger.println("Call " + member + " pm for "
+ mixDataSource.toAbbreviatedString()
+ " s3 " + spatialValues[3]);
}
......@@ -437,7 +437,7 @@ public class MixManager {
/*
* Subtract the current contribution from the mix
*/
if (contribution != null &&
if (contribution != null &&
mixDataSource.contributionIsInCommonMix()) {
WhisperGroup.mixData(contribution, outData, false);
......@@ -453,7 +453,7 @@ public class MixManager {
}
contribution = sa.generateSpatialAudio(
mixDataSource.getSourceId(),
mixDataSource.getSourceId(),
mixDataSource.getPreviousContribution(),
contribution, spatialValues);
}
......@@ -484,8 +484,8 @@ public class MixManager {
}
/*
* We know there are two MixDescriptors and the first one is
* the conference Mix and the second one is for subtracting
* We know there are two MixDescriptors and the first one is
* the conference Mix and the second one is for subtracting
* out the member's own data.
*/
private int[] fastMix() {
......@@ -508,26 +508,28 @@ public class MixManager {
memberMixDescriptor.getMixDataSource().getCurrentContribution();
if (memberContribution == null) {
System.arraycopy(conferenceMixContribution, 0, outData, 0,
conferenceMixContribution.length);
if (Logger.logLevel == -39) {
if (outData.length <= conferenceMixContribution.length)
System.arraycopy(conferenceMixContribution, 0, outData, 0, outData.length);
else
System.arraycopy(conferenceMixContribution, 0, outData, 0, conferenceMixContribution.length);
if (Logger.logLevel == -39) {
checkData(outData, useFastMix);
}
AudioConversion.clip(outData);
AudioConversion.clip(outData);
return outData;
}
WhisperGroup.mixData(conferenceMixContribution, memberContribution,
outData);
WhisperGroup.mixData(conferenceMixContribution, memberContribution, outData);
if (Logger.logLevel == -39) {
if (Logger.logLevel == -39) {
checkData(outData, useFastMix);
}
AudioConversion.clip(outData);
return outData;
return outData;
}
private void checkData(int[] data, boolean useFastMix) {
......@@ -536,7 +538,7 @@ public class MixManager {
Logger.println("Call " + member + " Non-zero data at " + i);
Logger.println("Call " + member
+ " useFastMix " + useFastMix);
Logger.println("Call " + member + " "
Logger.println("Call " + member + " "
+ toAbbreviatedString());
if (mixDescriptors.size() != 2 && useFastMix == true) {
Logger.println("useFastMix should be false!!!");
......@@ -559,7 +561,7 @@ public class MixManager {
for (int i = 0; i < data.length; i++) {
data[i] = 0; // optimize when volume is 0
}
return;
return;
}
for (int i = 0; i < data.length; i++) {
......@@ -593,9 +595,9 @@ public class MixManager {
s += " " + mixDescriptor.toAbbreviatedString();
if (member.getWhisperGroup() ==
if (member.getWhisperGroup() ==
mixDescriptor.getMixDataSource()) {
s += " + ";
}
......
......@@ -74,12 +74,11 @@ public class SipIncomingCallAgent extends CallSetupAgent implements SipListener
sipServerCallback = SipServer.getSipServerCallback();
MediaInfo mixerMediaPreference =
callHandler.getConferenceManager().getMediaInfo();
MediaInfo mixerMediaPreference = callHandler.getConferenceManager().getMediaInfo();
sipUtil = new SipUtil(mixerMediaPreference);
handleInvite((RequestEvent)o);
handleInvite((RequestEvent)o);
}
/**
......
......@@ -87,8 +87,7 @@ public class SipTPCCallAgent extends CallSetupAgent implements SipListener {
public SipTPCCallAgent(CallHandler callHandler) {
super(callHandler);
MediaInfo mixerMediaPreference =
callHandler.getConferenceManager().getMediaInfo();
MediaInfo mixerMediaPreference = callHandler.getConferenceManager().getMediaInfo();
sipUtil = new SipUtil(mixerMediaPreference);
}
......
......@@ -71,28 +71,30 @@ public class SipUtil {
private SdpManager sdpManager;
public SipUtil() {
this(null);
this(null);
}
public SipUtil(MediaInfo mediaInfo) {
if (!initialized) {
initialize();
}
public SipUtil(MediaInfo mediaInfo)
{
if (!initialized) {
initialize();
}
sdpManager = new SdpManager();
sdpManager = new SdpManager();
if (mediaInfo == null) {
try {
mediaInfo = sdpManager.findMediaInfo(RtpPacket.PCMU_ENCODING,
8000, 1);
} catch (ParseException e) {
Logger.println(
"SipUtil: Invalid media info, can't set preference"
+ e.getMessage());
}
}
if (mediaInfo == null)
{
try {
mediaInfo = sdpManager.findMediaInfo(RtpPacket.PCMU_ENCODING, 8000, 1);
Logger.println("SipUtil: Preference default media " + mediaInfo);
} catch (ParseException e) {
Logger.println("SipUtil: Invalid media info, can't set preference" + e.getMessage());
}
}
sdpManager.setPreferredMediaInfo(mediaInfo);
sdpManager.setPreferredMediaInfo(mediaInfo);
}
/**
......@@ -1042,9 +1044,8 @@ if (false) {
return getSdpInfo(sdpBody, true);
}
public SdpInfo getSdpInfo(String sdpBody, boolean isRequest)
throws ParseException {
public SdpInfo getSdpInfo(String sdpBody, boolean isRequest) throws ParseException
{
SdpInfo remoteSdpInfo = sdpManager.parseSdp(sdpBody);
MediaInfo myPreferredMediaInfo = sdpManager.getPreferredMediaInfo();
......@@ -1065,18 +1066,16 @@ if (false) {
Logger.println("My preferred payload being used " + payload);
} else {
if (isRequest) {
Logger.writeFile("My preferred media "
+ myPreferredMediaInfo + " not supported...");
Logger.writeFile("My preferred media " + myPreferredMediaInfo + " not supported...");
}
try {
payload = remoteSdpInfo.getMediaInfo().getPayload();
payload = remoteSdpInfo.getMediaInfo().getPayload();
remoteSdpInfo.setMediaInfo(sdpManager.findMediaInfo(payload));
Logger.writeFile("media setting is "
+ remoteSdpInfo.getMediaInfo());
Logger.writeFile("media setting is " + remoteSdpInfo.getMediaInfo());
} catch (ParseException e) {
throw new ParseException("Unsupported media "
+ remoteSdpInfo.getMediaInfo(), 0);
remoteSdpInfo.setMediaInfo(new MediaInfo((byte)0, RtpPacket.PCMU_ENCODING, 8000, 1, false));
}
}
return remoteSdpInfo;
......
......@@ -3,12 +3,12 @@
*
* This file is part of jVoiceBridge.
*
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* to you.
*
* jVoiceBridge is distributed in the hope that it will be useful,
* jVoiceBridge is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
......@@ -17,8 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied this
* code.
* exception as provided by Sun in the License file that accompanied this
* code.
*/
package com.sun.voip.server;
......@@ -143,7 +143,7 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
}
members.add(member);
}
}
public void removeCall(ConferenceMember member) {
if (members.contains(member) == false) {
......@@ -171,11 +171,11 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
whisperers.add(member);
if (Logger.logLevel >= Logger.LOG_INFO) {
Logger.println("Call " + member + " started whispering to "
Logger.println("Call " + member + " started whispering to "
+ id);
}
return;
}
}
if (whisperers.contains(member) == false) {
Logger.println("Call " + member + " is not in whisperers!");
......@@ -233,21 +233,22 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
* This is called when data is received from a conference member
* The member has already converted its contribution to linear.
*/
public void addToLinearDataMix(int[] contribution, boolean doNotRecord) {
if (doNotRecord) {
if (doNotRecordMix == null) {
doNotRecordMix = new int[contribution.length];
System.arraycopy(contribution, 0, doNotRecordMix, 0,
contribution.length);
return;
}
mixData(contribution, doNotRecordMix, true);
return;
}
public void addToLinearDataMix(int[] contribution, boolean doNotRecord)
{
if (doNotRecord) {
if (doNotRecordMix == null) {
doNotRecordMix = new int[contribution.length];
System.arraycopy(contribution, 0, doNotRecordMix, 0,
contribution.length);
return;
}
mixData(contribution, doNotRecordMix, true);
return;
}
if (linearMixBuffer == null) {
if (linearMixBuffer == null) {
linearMixBuffer = new int[contribution.length];
System.arraycopy(contribution, 0, linearMixBuffer, 0,
......@@ -258,39 +259,70 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
mixData(contribution, linearMixBuffer, true);
}
public static void mixData(int[] inData, int[] mixData, boolean add) {
try {
if (add) {
for (int i = 0; i < inData.length; i++) {
mixData[i] = mixData[i] + inData[i];
}
} else {
for (int i = 0; i < inData.length; i++) {
mixData[i] = mixData[i] - inData[i];
}
}
} catch (IndexOutOfBoundsException e) {
Logger.println("Exception! inData length " + inData.length
+ " mixData length " + mixData.length + " add " + add);
public static void mixData(int[] inData, int[] mixData, boolean add)
{
//Logger.println("mixData - inData length " + inData.length + " mixData length " + mixData.length + " add " + add);
e.printStackTrace();
}
try {
if (add) {
if (inData.length <= mixData.length)
{
for (int i = 0; i < inData.length; i++) {
mixData[i] = mixData[i] + inData[i];
}
} else {
for (int i = 0; i < mixData.length; i++) {
mixData[i] = mixData[i] + inData[i];
}
}
} else {
if (inData.length <= mixData.length)
{
for (int i = 0; i < inData.length; i++) {
mixData[i] = mixData[i] - inData[i];
}
} else {
for (int i = 0; i < mixData.length; i++) {
mixData[i] = mixData[i] - inData[i];
}
}
}
} catch (IndexOutOfBoundsException e) {
Logger.println("Exception! inData length " + inData.length
+ " mixData length " + mixData.length + " add " + add);
e.printStackTrace();
}
}
public static void mixData(int[] conferenceData, int[] memberData,
int[] outData) {
public static void mixData(int[] conferenceData, int[] memberData, int[] outData)
{
//Logger.println("mixData - conferenceData length " + conferenceData.length +" memberData.length " + memberData.length + " outData length " + outData.length);
try {
for (int i = 0; i < outData.length; i ++) {
outData[i] = conferenceData[i] - memberData[i];
}
} catch (IndexOutOfBoundsException e) {
Logger.println("Exception! conferenceData length "
+ conferenceData.length +" memberData.length "
+ memberData.length + " outData length " + outData.length);
try {
if (outData.length <= memberData.length)
{
for (int i = 0; i < outData.length; i ++) {
outData[i] = conferenceData[i] - memberData[i];
}
e.printStackTrace();
}
} else {
for (int i = 0; i < memberData.length; i ++) {
outData[i] = conferenceData[i] - memberData[i];
}
}
} catch (IndexOutOfBoundsException e) {
Logger.println("mixData Exception! conferenceData length " + conferenceData.length +" memberData.length " + memberData.length + " outData length " + outData.length);
e.printStackTrace();
}
}
private int[] previousContribution;
......@@ -321,8 +353,8 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
synchronized (conferenceTreatments) {
currentTreatment.saveCurrentContribution();
int[] treatmentData = currentTreatment.getCurrentContribution();
int[] treatmentData = currentTreatment.getCurrentContribution();
if (treatmentDone) {
conferenceTreatments.remove(currentTreatment);
......@@ -442,7 +474,7 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
if (audioRecorder == null) {
synchronized (recordingLock) {
audioRecorder = new Recorder(recordingFile, "au",
audioRecorder = new Recorder(recordingFile, "au",
mediaInfo);
Logger.println("starting conference recorder for "
......@@ -553,7 +585,7 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
s += " ";
for (int i = 0; i < members.size(); i++) {
ConferenceMember member =
ConferenceMember member =
(ConferenceMember)members.get(i);
CallParticipant cp = member.getCallParticipant();
......@@ -564,7 +596,7 @@ public class WhisperGroup implements MixDataSource, TreatmentDoneListener {
}
s += " ";
}
}
return s;
}
......
......@@ -558,7 +558,6 @@ public class RayoComponent extends AbstractComponent
{
if (channel == null)
{
cp.setMediaPreference("PCMU/8000/1");
cp.setPhoneNumber(handset.sipuri);
cp.setAutoAnswer(true);
cp.setProtocol("SIP");
......@@ -1037,7 +1036,6 @@ public class RayoComponent extends AbstractComponent
CallParticipant cp = new CallParticipant();
cp.setVoiceDetection(true);
cp.setCallOwner(handsetId);
cp.setMediaPreference("PCMU/8000/1");
cp.setProtocol("SIP");
cp.setDisplayName(callerName);
cp.setPhoneNumber(to);
......@@ -1063,6 +1061,7 @@ public class RayoComponent extends AbstractComponent
CallParticipant hp = handsetHandler.getCallParticipant();
headers.put("mixer_name", hp.getConferenceId());
headers.put("codec_name", "PCM/48000/2".equals(hp.getMediaPreference()) ? "OPUS" : "PCMU");
try {
ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(hp.getConferenceId());
......@@ -1225,6 +1224,7 @@ public class RayoComponent extends AbstractComponent
ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(mixer);
cp.setConferenceId(mixer);
cp.setCallId(mixer);
cp.setMediaPreference(hp.getMediaPreference());
conferenceManager.setCallId(mixer);
conferenceManager.setTransferCall(transferCall);
......@@ -1761,68 +1761,65 @@ public class RayoComponent extends AbstractComponent
public boolean routeIncomingSIP(CallParticipant cp)
{
Log.info("Incoming SIP, call route to user " + cp.getToPhoneNumber());
boolean canRoute = false;
Group group = null;
JID foundUser = findUser(cp.getToPhoneNumber());
String callId = "rayo-incoming-" + System.currentTimeMillis();
cp.setCallId(callId);
cp.setMediaPreference("PCMU/8000/1");
cp.setConferenceId(callId);
if (foundUser != null)
canRoute = true;
else {
try {
group = GroupManager.getInstance().getGroup(cp.getToPhoneNumber());
canRoute = true;
ConferenceManager conferenceManager = ConferenceManager.getConference(callId, cp.getMediaPreference(), cp.getToPhoneNumber(), false);
conferenceManager.setCallId(callId);
} catch (GroupNotFoundException e) {
Map<String, String> headers = cp.getHeaders();
headers.put("mixer_name", callId);
headers.put("call_protocol", "SIP");
headers.put("group_name", cp.getToPhoneNumber());
if (foundUser != null) // send this call to specific user
{
cp.setCallOwner(foundUser.toString());
routeSIPCall(foundUser, cp, callId, headers);
return true;
}
}
try {
Group group = GroupManager.getInstance().getGroup(cp.getToPhoneNumber());
Log.info("Incoming SIP, call route to entity " + cp.getToPhoneNumber() + " " + canRoute);
conferenceManager.setGroupName(cp.getToPhoneNumber());
if (canRoute)
{
String callId = "rayo-incoming-" + System.currentTimeMillis();
cp.setCallId(callId);
cp.setConferenceId(callId);
for (JID memberJID : group.getMembers())
{
Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(memberJID.getNode());
if (cp.getMediaPreference() == null) cp.setMediaPreference("PCMU/8000/1"); // regular phone
for (ClientSession session : sessions)
{
routeSIPCall(session.getAddress(), cp, callId, headers);
}
}
ConferenceManager conferenceManager = ConferenceManager.getConference(callId, cp.getMediaPreference(), cp.getToPhoneNumber(), false);
conferenceManager.setCallId(callId);
return true;
Map<String, String> headers = cp.getHeaders();
headers.put("mixer_name", callId);
headers.put("call_protocol", "SIP");
headers.put("codec_name", "PCM/48000/2".equals(cp.getMediaPreference()) ? "OPUS" : "PCMU");
headers.put("group_name", cp.getToPhoneNumber());
} catch (GroupNotFoundException e) {
// Group not found
if (foundUser != null) // send this call to specific user
{
cp.setCallOwner(foundUser.toString());
routeSIPCall(foundUser, cp, callId, headers);
if (XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").hasChatRoom(cp.getToPhoneNumber())) {
} else {
MUCRoom room = XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").getChatRoom(cp.getToPhoneNumber());
conferenceManager.setGroupName(cp.getToPhoneNumber());
if (room != null)
for (JID memberJID : group.getMembers())
{
for (MUCRole role : room.getOccupants())
Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(memberJID.getNode());
for (ClientSession session : sessions)
{
routeSIPCall(role.getUserAddress(), cp, callId, headers);
routeSIPCall(session.getAddress(), cp, callId, headers);
}
}
return true;
} else {
return false;
}
}
return canRoute;
}
public void routeSIPCall(JID callee, CallParticipant cp, String callId, Map<String, String> headers)
......
......@@ -91,7 +91,6 @@ public class RelayChannel {
private Integer lastAudioTimestamp = new Integer((int)0);
private long decoder = 0;
private long encoder = 0;
private final int sampleRate = 48000;
private final int frameSizeInMillis = 20;
private final int outputFrameSize = 2;
......@@ -297,22 +296,13 @@ public class RelayChannel {
encryptor2 = new Encryptor(SDPCryptoSuite.getEncryptionMode(handset.cryptoSuite), remoteCryptoKey, remoteCryptoSalt, localCryptoKey, localCryptoSalt);
decoder = Opus.decoder_create(sampleRate, channels);
encoder = Opus.encoder_create(sampleRate, channels);
//Opus.encoder_set_bandwidth(encoder, Opus.OPUS_AUTO);
//Opus.encoder_set_bitrate(encoder, 32000);
//Opus.encoder_set_complexity(encoder, 10);
//Opus.encoder_set_inband_fec(encoder, 1);
//Opus.encoder_set_packet_loss_perc(encoder, 1);
//Opus.encoder_set_dtx(encoder, 1);
if (decoder == 0) Log.error( "Opus decoder creation error ");
if (encoder == 0) Log.error( "Opus encoder creation error ");
if (decoder == 0 || encoder == 0)
if (decoder == 0)
{
handset.codec = "PCMU";
Log.warn( "Opus encoder/decoder creation failure, PCMU will be used in default");
Log.warn( "Opus decoder creation failure, PCMU will be used in default");
}
} catch (Exception e) {
......@@ -361,12 +351,6 @@ public class RelayChannel {
decoder = 0;
}
if (encoder != 0)
{
Opus.encoder_destroy(encoder);
encoder = 0;
}
SayCompleteEvent complete = new SayCompleteEvent();
complete.setReason(SayCompleteEvent.Reason.valueOf("SUCCESS"));
......@@ -445,7 +429,7 @@ public class RelayChannel {
return new Long((new Integer(timestamp.intValue())).longValue());
}
public synchronized void pushAudio(byte[] rtpData, int[] in)
public synchronized void pushAudio(byte[] rtpData, byte[] opus)
{
try {
......@@ -454,15 +438,9 @@ public class RelayChannel {
RTPPacket newPacket = RTPPacket.parseBytes(BitAssistant.bytesToArray(rtpData));
RTPPacket packet = RTPPacket.parseBytes(lastAudioPacket.getBytes());
if (handset.codec == null || "OPUS".equals(handset.codec))
if (opus != null)
{
byte[] input = AudioConversion.littleEndianIntsToBytes(in);
byte[] output = new byte[Opus.MAX_PACKET];
int outLength = Opus.encode(encoder, input, 0, frameSizeInSamplesPerChannel, output, 0, output.length);
byte[] compressedBytes = new byte[outLength];
System.arraycopy(output, 0, compressedBytes, 0, outLength);
packet.setPayload(BitAssistant.bytesToArray(compressedBytes));
packet.setPayload(BitAssistant.bytesToArray(opus));
packet.setTimestamp(getNextAudioTimestamp(Long.valueOf(48000)));
} else { // ULAW
......@@ -484,8 +462,8 @@ public class RelayChannel {
kt++;
if ( kt < 10 ) {
Log.info( "+++ " + in );
if ( kt < 20 ) {
Log.info( "+++ " + packet.getPayload().length );
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment