1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2024-12-27 14:47:59 +01:00

STUN fix and Media Tunning

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@7610 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Thiago Camargo 2007-03-20 18:52:57 +00:00 committed by thiago
parent cb2df91084
commit 9627fef139
9 changed files with 239 additions and 126 deletions

View file

@ -97,16 +97,17 @@ public class IncomingJingleSession extends JingleSession {
/**
* Constructor for a Jingle incoming session
*
* @param conn the XMPP connection
* @param responder the responder
* @param transportManager The transport manager
* @param conn the XMPP connection
* @param responder the responder
* @param transportManager The transport manager
* @param initialJingleSessionRequest the initial Jingle Session Request
*/
protected IncomingJingleSession(XMPPConnection conn, String responder,
List payloadTypes, JingleTransportManager transportManager, String sid) {
List payloadTypes, JingleTransportManager transportManager, JingleSessionRequest initialJingleSessionRequest) throws XMPPException {
super(conn, responder, conn.getUser());
setSid(sid);
setSid(initialJingleSessionRequest.getSessionID());
// Create the states...
@ -130,19 +131,38 @@ public class IncomingJingleSession extends JingleSession {
setTransportNeg(new TransportNegotiator.Ice(this, resolver));
}
// Establish the default state
setState(accepting);
updatePacketListener();
Jingle packet = initialJingleSessionRequest.getJingle();
if (packet != null) {
// Initialize the session information
setSid(packet.getSid());
respond(packet);
}
else {
throw new XMPPException(
"Session request with null Jingle packet.");
}
}
/**
* Constructor for a Jingle Incoming session with a defined Media Manager
*
* @param conn the XMPP connection
* @param responder the responder
* @param transportManager The transport manager
* @param jingleMediaManager The Media Manager for this Session
* @param conn the XMPP connection
* @param responder the responder
* @param transportManager The transport manager
* @param jingleMediaManager The Media Manager for this Session
* @param initialJingleSessionRequest the initial Jingle Session Request
*/
protected IncomingJingleSession(XMPPConnection conn, String responder,
List payloadTypes, JingleTransportManager transportManager, JingleMediaManager jingleMediaManager, String sid) {
this(conn, responder, payloadTypes, transportManager, sid);
List payloadTypes, JingleTransportManager transportManager, JingleMediaManager jingleMediaManager, JingleSessionRequest initialJingleSessionRequest) throws XMPPException {
this(conn, responder, payloadTypes, transportManager, initialJingleSessionRequest);
this.jingleMediaManager = jingleMediaManager;
}
@ -153,27 +173,6 @@ public class IncomingJingleSession extends JingleSession {
* @throws XMPPException
*/
public void start(JingleSessionRequest initialJingleSessionRequest) throws XMPPException {
if (invalidState()) {
Jingle packet = initialJingleSessionRequest.getJingle();
if (packet != null) {
// Initialize the session information
setSid(packet.getSid());
// Establish the default state
setState(accepting);
updatePacketListener();
respond(packet);
}
else {
throw new IllegalStateException(
"Session request with null Jingle packet.");
}
}
else {
throw new IllegalStateException("Starting session without null state.");
}
}
/**
@ -182,7 +181,15 @@ public class IncomingJingleSession extends JingleSession {
* @throws XMPPException
*/
public void start() throws XMPPException {
start(this.getInitialSessionRequest());
}
/**
* Force a call acceptance. Used to accept a hooked call.
* @deprecated Avoid to use this method. Not compliance.
*/
public void accept(){
setState(active);
}
/**

View file

@ -649,10 +649,10 @@ public class JingleManager implements JingleSessionListener {
if (jingleMediaManager != null)
session = new IncomingJingleSession(connection, request
.getFrom(), payloadTypes, jingleTransportManager, jingleMediaManager, request.getSessionID());
.getFrom(), payloadTypes, jingleTransportManager, jingleMediaManager, request);
else
session = new IncomingJingleSession(connection, request
.getFrom(), payloadTypes, jingleTransportManager, request.getSessionID());
.getFrom(), payloadTypes, jingleTransportManager, request);
triggerSessionCreated(session);

View file

@ -124,7 +124,7 @@ public abstract class JingleSession extends JingleNegotiator {
* @param jingleMediaManager the jingleMediaManager
*/
protected JingleSession(XMPPConnection conn, String initiator, String responder,
String sessionid, JingleMediaManager jingleMediaManager) {
String sessionid, JingleMediaManager jingleMediaManager) {
super(conn);
this.mediaNeg = null;
@ -389,21 +389,25 @@ public abstract class JingleSession extends JingleNegotiator {
if (invalidState()) {
throw new IllegalStateException(
"Illegal state in dispatch packet in Session manager.");
} else {
}
else {
if (iq == null) {
// If there is no input packet, then we must be inviting...
jout = getState().eventInvite();
} else {
}
else {
if (iq.getType().equals(IQ.Type.ERROR)) {
// Process errors
getState().eventError(iq);
} else if (iq.getType().equals(IQ.Type.RESULT)) {
}
else if (iq.getType().equals(IQ.Type.RESULT)) {
// Process ACKs
if (isExpectedId(iq.getPacketID())) {
jout = getState().eventAck(iq);
removeExpectedId(iq.getPacketID());
}
} else if (iq instanceof Jingle) {
}
else if (iq instanceof Jingle) {
// It is not an error: it is a Jingle packet...
Jingle jin = (Jingle) iq;
Jingle.Action action = jin.getAction();
@ -411,17 +415,22 @@ public abstract class JingleSession extends JingleNegotiator {
if (action != null) {
if (action.equals(Jingle.Action.SESSIONACCEPT)) {
jout = getState().eventAccept(jin);
} else if (action.equals(Jingle.Action.SESSIONINFO)) {
}
else if (action.equals(Jingle.Action.SESSIONINFO)) {
jout = getState().eventInfo(jin);
} else if (action.equals(Jingle.Action.SESSIONINITIATE)) {
}
else if (action.equals(Jingle.Action.SESSIONINITIATE)) {
if (getState() != null)
jout = getState().eventInitiate(jin);
} else if (action.equals(Jingle.Action.SESSIONREDIRECT)) {
}
else if (action.equals(Jingle.Action.SESSIONREDIRECT)) {
jout = getState().eventRedirect(jin);
} else if (action.equals(Jingle.Action.SESSIONTERMINATE)) {
}
else if (action.equals(Jingle.Action.SESSIONTERMINATE)) {
jout = getState().eventTerminate(jin);
}
} else {
}
else {
jout = errorMalformedStanza(iq);
}
}
@ -506,7 +515,7 @@ public abstract class JingleSession extends JingleNegotiator {
* @return the new Jingle packet
*/
private Jingle sendJingleParts(IQ iq, Jingle jSes, Jingle jDesc,
Jingle jTrans) {
Jingle jTrans) {
Jingle response = null;
if (jSes != null) {
@ -514,7 +523,8 @@ public abstract class JingleSession extends JingleNegotiator {
jSes.addTransports(jTrans.getTransportsList());
response = sendFormattedJingle(iq, jSes);
} else {
}
else {
// If we don't have a valid session message, then we must send
// separated messages for transport and jmf...
if (jDesc != null) {
@ -580,7 +590,8 @@ public abstract class JingleSession extends JingleNegotiator {
if (jout.getTo() == null) {
if (iq != null) {
jout.setTo(iq.getFrom());
} else {
}
else {
jout.setTo(other);
}
}
@ -588,7 +599,8 @@ public abstract class JingleSession extends JingleNegotiator {
if (jout.getFrom() == null) {
if (iq != null) {
jout.setFrom(iq.getTo());
} else {
}
else {
jout.setFrom(me);
}
}
@ -681,7 +693,8 @@ public abstract class JingleSession extends JingleNegotiator {
if (jda.size() > 1) {
throw new XMPPException(
"Unsupported feature: the number of accepted content descriptions is greater than 1.");
} else if (jda.size() == 1) {
}
else if (jda.size() == 1) {
JingleContentDescription jd = (JingleContentDescription) jda.get(0);
if (jd.getJinglePayloadTypesCount() > 1) {
throw new XMPPException(
@ -713,13 +726,15 @@ public abstract class JingleSession extends JingleNegotiator {
if (jta.size() > 1) {
throw new XMPPException(
"Unsupported feature: the number of accepted transports is greater than 1.");
} else if (jta.size() == 1) {
}
else if (jta.size() == 1) {
org.jivesoftware.smackx.packet.JingleTransport jt = (org.jivesoftware.smackx.packet.JingleTransport) jta.get(0);
if (jt.getCandidatesCount() > 1) {
throw new XMPPException(
"Unsupported feature: the number of accepted transport candidates is greater than 1.");
} else if (jt.getCandidatesCount() == 1) {
}
else if (jt.getCandidatesCount() == 1) {
JingleTransportCandidate jtc = (JingleTransportCandidate) jt
.getCandidatesList().get(0);
acceptedLocalCandidate = jtc.getMediaTransport();
@ -761,7 +776,8 @@ public abstract class JingleSession extends JingleNegotiator {
if (other.initiator != null) {
return false;
}
} else if (!initiator.equals(other.initiator)) {
}
else if (!initiator.equals(other.initiator)) {
//Todo check behavior
// return false;
}
@ -770,7 +786,8 @@ public abstract class JingleSession extends JingleNegotiator {
if (other.responder != null) {
return false;
}
} else if (!responder.equals(other.responder)) {
}
else if (!responder.equals(other.responder)) {
return false;
}
@ -778,7 +795,8 @@ public abstract class JingleSession extends JingleNegotiator {
if (other.sid != null) {
return false;
}
} else if (!sid.equals(other.sid)) {
}
else if (!sid.equals(other.sid)) {
return false;
}
@ -922,12 +940,14 @@ public abstract class JingleSession extends JingleNegotiator {
System.out.println("Ignored Jingle(INI): " + iq.toXML());
return false;
}
} else {
}
else {
// We accept some non-Jingle IQ packets: ERRORs and ACKs
if (iq.getType().equals(IQ.Type.SET)) {
System.out.println("Ignored Jingle(TYPE): " + iq.toXML());
return false;
} else if (iq.getType().equals(IQ.Type.GET)) {
}
else if (iq.getType().equals(IQ.Type.GET)) {
System.out.println("Ignored Jingle(TYPE): " + iq.toXML());
return false;
}
@ -1038,7 +1058,7 @@ public abstract class JingleSession extends JingleNegotiator {
* Trigger a session established event.
*/
protected void triggerSessionEstablished(PayloadType pt,
TransportCandidate rc, TransportCandidate lc) {
TransportCandidate rc, TransportCandidate lc) {
ArrayList listeners = getListenersList();
Iterator iter = listeners.iterator();
while (iter.hasNext()) {
@ -1049,12 +1069,12 @@ public abstract class JingleSession extends JingleNegotiator {
}
}
if (jingleMediaManager != null) {
rc.removeCandidateEcho();
lc.removeCandidateEcho();
jingleMediaSession = jingleMediaManager.createMediaSession(pt, rc, lc);
if (jingleMediaSession != null) {
rc.removeCandidateEcho();
lc.removeCandidateEcho();
jingleMediaSession.startTrasmit();
jingleMediaSession.startReceive();
@ -1113,9 +1133,7 @@ public abstract class JingleSession extends JingleNegotiator {
*/
public void terminate() throws XMPPException {
if (isClosed()) return;
//remo
System.out.println("State: " + this.getState());
Jingle result = null;
Jingle jout = new Jingle(Jingle.Action.SESSIONTERMINATE);
jout.setType(IQ.Type.SET);
sendFormattedJingle(jout);
@ -1151,7 +1169,7 @@ public abstract class JingleSession extends JingleNegotiator {
* @return The created IQ packet.
*/
public static IQ createIQ(String ID, String to, String from,
IQ.Type type) {
IQ.Type type) {
IQ iqPacket = new IQ() {
public String getChildElementXML() {
return null;
@ -1177,7 +1195,7 @@ public abstract class JingleSession extends JingleNegotiator {
* @return The created IQ packet.
*/
public static IQ createError(String ID, String to, String from,
int errCode, String errStr) {
int errCode, String errStr) {
IQ iqError = createIQ(ID, to, from, IQ.Type.ERROR);
XMPPError error = new XMPPError(new XMPPError.Condition(errStr));

View file

@ -21,6 +21,8 @@ package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
import javax.media.*;
import javax.media.control.TrackControl;
import javax.media.control.PacketSizeControl;
import javax.media.control.BufferControl;
import javax.media.format.AudioFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
@ -57,7 +59,7 @@ public class AudioChannel {
private MediaLocator locator;
private String localIpAddress;
private String ipAddress;
private String remoteIpAddress;
private int localPort;
private int portBase;
private Format format;
@ -76,30 +78,25 @@ public class AudioChannel {
*
* @param locator media locator
* @param localIpAddress local IP address
* @param ipAddress remote IP address
* @param remoteIpAddress remote IP address
* @param localPort local port number
* @param remotePort remote port number
* @param format audio format
*/
public AudioChannel(MediaLocator locator,
String localIpAddress,
String ipAddress,
String remoteIpAddress,
int localPort,
int remotePort,
Format format) {
this.locator = locator;
this.localIpAddress = localIpAddress;
this.ipAddress = ipAddress;
this.remoteIpAddress = remoteIpAddress;
this.localPort = localPort;
this.portBase = remotePort;
this.format = format;
// Create a processor for the specified jmf locator
String result = createProcessor();
if (result != null) {
started = false;
}
}
/**
@ -111,8 +108,14 @@ public class AudioChannel {
*/
public synchronized String start() {
if (started) return null;
// Create a processor for the specified jmf locator
String result = createProcessor();
if (result != null) {
started = false;
}
started = true;
String result;
// Create an RTP session to transmit the output of the
// processor to the specified IP address and port no.
@ -121,7 +124,6 @@ public class AudioChannel {
processor.close();
processor = null;
started = false;
return result;
}
// Start the transmission
@ -226,6 +228,39 @@ public class AudioChannel {
tracks[i].setFormat(chosen);
System.err.println("Track " + i + " is set to transmit as:");
System.err.println(" " + chosen);
if (tracks[i].getFormat() instanceof AudioFormat) {
int packetRate = 20;
PacketSizeControl pktCtrl = (PacketSizeControl) processor.getControl(PacketSizeControl.class.getName());
if (pktCtrl != null) {
try {
pktCtrl.setPacketSize(getPacketSize(tracks[i].getFormat(), packetRate));
}
catch (IllegalArgumentException e) {
pktCtrl.setPacketSize(80);
// Do nothing
}
}
if (tracks[i].getFormat().getEncoding().equals(AudioFormat.ULAW_RTP)) {
Codec codec[] = new Codec[3];
codec[0] = new com.ibm.media.codec.audio.rc.RCModule();
codec[1] = new com.ibm.media.codec.audio.ulaw.JavaEncoder();
codec[2] = new com.sun.media.codec.audio.ulaw.Packetizer();
((com.sun.media.codec.audio.ulaw.Packetizer) codec
[2]).setPacketSize(160);
try {
tracks[i].setCodecChain(codec);
}
catch (UnsupportedPlugInException e) {
e.printStackTrace();
}
}
}
atLeastOneTrack = true;
}
else
@ -249,6 +284,28 @@ public class AudioChannel {
return null;
}
/**
* Get the best packet size for a given codec and a codec rate
*
* @param codecFormat
* @param milliseconds
* @return
* @throws IllegalArgumentException
*/
private int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException {
String encoding = codecFormat.getEncoding();
if (encoding.equalsIgnoreCase(AudioFormat.GSM) ||
encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) {
return milliseconds * 4; // 1 byte per millisec
}
else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) ||
encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) {
return milliseconds * 8;
}
else {
throw new IllegalArgumentException("Unknown codec type");
}
}
/**
* Use the RTPManager API to create sessions for each jmf
@ -274,7 +331,7 @@ public class AudioChannel {
rtpMgrs[i] = RTPManager.newInstance();
port = portBase + 2 * i;
ipAddr = InetAddress.getByName(ipAddress);
ipAddr = InetAddress.getByName(remoteIpAddress);
localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress),
localPort);
@ -284,11 +341,17 @@ public class AudioChannel {
rtpMgrs[i].addReceiveStreamListener(audioReceiver);
rtpMgrs[i].addSessionListener(audioReceiver);
BufferControl bc = (BufferControl) rtpMgrs[i].getControl("javax.media.control.BufferControl");
if (bc != null) {
int bl = 160;
bc.setBufferLength(bl);
}
rtpMgrs[i].initialize(localAddr);
rtpMgrs[i].addTarget(destAddr);
System.err.println("Created RTP session at " + localPort + " to: " + ipAddress + " " + port);
System.err.println("Created RTP session at " + localPort + " to: " + remoteIpAddress + " " + port);
sendStream = rtpMgrs[i].createSendStream(dataOutput, i);

View file

@ -41,7 +41,7 @@ import java.net.ServerSocket;
public class AudioMediaSession extends JingleMediaSession {
private AudioChannel audioChannel;
private String locator = "javasound://";
private String locator = "dsound://";
/**
* Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
@ -52,7 +52,7 @@ public class AudioMediaSession extends JingleMediaSession {
*/
public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
final TransportCandidate local) {
this(payloadType, remote, local, "javasound://");
this(payloadType, remote, local, "dsound://");
}
/**

View file

@ -42,7 +42,11 @@ public class BridgedTransportManager extends JingleTransportManager implements J
this.xmppConnection = xmppConnection;
}
// Return the correspondent resolver
/**
* Return the correspondent resolver
* @param session correspondent Jingle Session
* @return resolver
*/
protected TransportResolver createResolver(JingleSession session) {
BridgedResolver bridgedResolver = new BridgedResolver(this.xmppConnection);
return bridgedResolver;

View file

@ -176,64 +176,65 @@ public class ICEResolver extends TransportResolver {
e.printStackTrace();
}
}
// Get Public Candidate From XMPP Server
// Get Public Candidate From XMPP Server
if (iceNegociator.getPublicCandidate() == null) {
if (iceNegociator.getPublicCandidate() == null) {
String publicIp = RTPBridge.getPublicIP(connection);
String publicIp = RTPBridge.getPublicIP(connection);
if (publicIp != null && !publicIp.equals("")) {
if (publicIp != null && !publicIp.equals("")) {
Enumeration ifaces = null;
Enumeration ifaces = null;
try {
ifaces = NetworkInterface.getNetworkInterfaces();
}
catch (SocketException e) {
e.printStackTrace();
}
try {
ifaces = NetworkInterface.getNetworkInterfaces();
}
catch (SocketException e) {
e.printStackTrace();
}
// If detect this address in local machine, don't use it.
// If detect this address in local machine, don't use it.
boolean found = false;
boolean found = false;
while (ifaces.hasMoreElements() && !false) {
while (ifaces.hasMoreElements() && !false) {
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
Enumeration iaddresses = iface.getInetAddresses();
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
Enumeration iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = (InetAddress) iaddresses.nextElement();
if (iaddress.getHostAddress().indexOf(publicIp) > -1) {
found = true;
break;
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = (InetAddress) iaddresses.nextElement();
if (iaddress.getHostAddress().indexOf(publicIp) > -1) {
found = true;
break;
}
}
}
}
if (!found) {
try {
TransportCandidate publicCandidate = new ICECandidate(
publicIp, 1, 0, String.valueOf(Math.abs(random.nextLong())), getFreePort(), "1", 0, "srflx");
publicCandidate.setLocalIp(InetAddress.getLocalHost().getHostAddress());
if (!found) {
try {
publicCandidate.addCandidateEcho(session);
TransportCandidate publicCandidate = new ICECandidate(
publicIp, 1, 0, String.valueOf(Math.abs(random.nextLong())), getFreePort(), "1", 0, "srflx");
publicCandidate.setLocalIp(InetAddress.getLocalHost().getHostAddress());
try {
publicCandidate.addCandidateEcho(session);
}
catch (SocketException e) {
e.printStackTrace();
}
addCandidate(publicCandidate);
}
catch (SocketException e) {
catch (UnknownHostException e) {
e.printStackTrace();
}
addCandidate(publicCandidate);
}
catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
}
this.setResolveEnd();
}

View file

@ -512,6 +512,8 @@ public class RTPBridge extends IQ {
// Cancel the collector.
collector.cancel();
if(response == null) return null;
if (response.getIp() == null || response.getIp().equals("")) return null;
Enumeration ifaces = null;

View file

@ -21,15 +21,13 @@
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.IncomingJingleSession;
import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
import org.jivesoftware.smackx.jingle.OutgoingJingleSession;
import org.jivesoftware.smackx.jingle.*;
import org.jivesoftware.smackx.jingle.mediaimpl.jmf.JmfMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.jmf.AudioChannel;
import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.multi.MultiMediaManager;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionStateListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.nat.BridgedTransportManager;
import org.jivesoftware.smackx.jingle.nat.ICETransportManager;
@ -56,7 +54,7 @@ public class JingleMediaTest extends SmackTestCase {
XMPPConnection x0 = getConnection(0);
XMPPConnection x1 = getConnection(1);
for (int i = 0; i < 3; i++)
for (int i = 0; i < 1; i++)
try {
ICETransportManager icetm0 = new ICETransportManager(x0, "jivesoftware.com", 3478);
@ -78,10 +76,22 @@ public class JingleMediaTest extends SmackTestCase {
JingleSessionRequestListener jingleSessionRequestListener = new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
IncomingJingleSession session = request.accept(jm1.getMediaManager().getPayloads());
session.start(request);
session.addStateListener(new JingleSessionStateListener() {
public void beforeChange(JingleNegotiator.State old, JingleNegotiator.State newOne) throws JingleNegotiator.JingleException {
if (newOne instanceof IncomingJingleSession.Active) {
throw new JingleNegotiator.JingleException();
}
}
public void afterChanged(JingleNegotiator.State old, JingleNegotiator.State newOne) {
}
});
}
catch (XMPPException e) {
e.printStackTrace();
@ -96,12 +106,19 @@ public class JingleMediaTest extends SmackTestCase {
js0.start();
Thread.sleep(50000);
Thread.sleep(20000);
IncomingJingleSession incomingJingleSession = (IncomingJingleSession) jm1.getSession(js0.getConnection().getUser());
incomingJingleSession.removeAllStateListeners();
incomingJingleSession.accept();
Thread.sleep(15000);
js0.terminate();
jm1.removeJingleSessionRequestListener(jingleSessionRequestListener);
Thread.sleep(6000);
Thread.sleep(60000);
}
catch (Exception e) {
@ -139,6 +156,7 @@ public class JingleMediaTest extends SmackTestCase {
MultiMediaManager jingleMediaManager0 = new MultiMediaManager();
jingleMediaManager0.addMediaManager(new JmfMediaManager());
jingleMediaManager0.addMediaManager(new SpeexMediaManager());
jingleMediaManager0.setPreferredPayloadType(jingleMediaManager0.getPayloads().get(1));
MultiMediaManager jingleMediaManager1 = new MultiMediaManager();
jingleMediaManager1.addMediaManager(new JmfMediaManager());
jingleMediaManager1.addMediaManager(new SpeexMediaManager());