1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-12-08 14:11:07 +01:00

Jingle Extension added to Smack Repository

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@6517 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Thiago Camargo 2007-01-04 17:25:30 +00:00 committed by thiago
parent f1972c2571
commit 4b6de6647b
104 changed files with 16411 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4" relativePaths="true" type="JAVA_MODULE">
<component name="ModuleRootManager" />
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/../../classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../lib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../lib/windows" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../test" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="JingleExtension" />
<orderEntry type="module" module-name="Smack" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/jmf.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/commons-logging-adapters-1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/jspeex-0.9.7-jfcom.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/commons-logging-api-1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/Speex.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/commons-logging-1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntryProperties />
</component>
</module>

View file

@ -0,0 +1,157 @@
package org.jivesoftware.demo;
import org.jivesoftware.jingleaudio.jmf.JmfMediaManager;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
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.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.nat.BridgedTransportManager;
import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingle.nat.RTPBridge;
import org.jivesoftware.smackx.jingle.nat.STUNTransportManager;
import javax.swing.*;
import java.awt.event.ActionEvent;
/**
* $RCSfile$
* $Revision: $
* $Date: 28/12/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class Demo extends JFrame {
private JingleTransportManager transportManager = null;
private XMPPConnection xmppConnection = null;
private String server = null;
private String user = null;
private String pass = null;
private JingleManager jm = null;
private IncomingJingleSession incoming = null;
private OutgoingJingleSession outgoing = null;
private JTextField jid = new JTextField(30);
public Demo(String server, String user, String pass) {
this.server = server;
this.user = user;
this.pass = pass;
xmppConnection = new XMPPConnection(server);
try {
xmppConnection.connect();
xmppConnection.login(user, pass);
initialize();
} catch (XMPPException e) {
e.printStackTrace();
}
}
public void initialize() {
if (RTPBridge.serviceAvailable(xmppConnection))
transportManager = new BridgedTransportManager(xmppConnection);
else
transportManager = new STUNTransportManager();
jm = new JingleManager(xmppConnection, transportManager, new JmfMediaManager());
if (transportManager instanceof BridgedTransportManager)
jm.addCreationListener((BridgedTransportManager) transportManager);
jm.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(JingleSessionRequest request) {
if (incoming != null)
return;
try {
// Accept the call
incoming = request.accept();
// Start the call
incoming.start();
}
catch (XMPPException e) {
e.printStackTrace();
}
}
});
createGUI();
}
public void createGUI() {
JPanel jPanel = new JPanel();
jPanel.add(jid);
jPanel.add(new JButton(new AbstractAction("Call") {
public void actionPerformed(ActionEvent e) {
if (outgoing != null) return;
try {
outgoing = jm.createOutgoingJingleSession(jid.getText());
} catch (XMPPException e1) {
e1.printStackTrace();
}
}
}));
jPanel.add(new JButton(new AbstractAction("Hangup") {
public void actionPerformed(ActionEvent e) {
if (outgoing != null)
try {
outgoing.terminate();
} catch (XMPPException e1) {
e1.printStackTrace();
} finally {
outgoing = null;
}
if (incoming != null)
try {
incoming.terminate();
} catch (XMPPException e1) {
e1.printStackTrace();
} finally {
incoming = null;
}
}
}));
this.add(jPanel);
}
public static void main(String args[]) {
Demo demo = null;
if (args.length > 2) {
demo = new Demo(args[0], args[1], args[2]);
demo.pack();
demo.setVisible(true);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
}

View file

@ -0,0 +1,439 @@
/**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.jingleaudio.jmf;
import javax.media.*;
import javax.media.control.TrackControl;
import javax.media.format.AudioFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.PushBufferDataSource;
import javax.media.protocol.PushBufferStream;
import javax.media.rtp.RTPManager;
import javax.media.rtp.SendStream;
import javax.media.rtp.SessionAddress;
import javax.media.rtp.rtcp.SourceDescription;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
/**
* An Easy to use Audio Channel implemented using JMF.
* It sends and receives jmf for and from desired IPs and ports.
* Also has a rport Symetric behavior for better NAT Traversal.
* It send data from a defined port and receive data in the same port, making NAT binds easier.
* <p/>
* Send from portA to portB and receive from portB in portA.
* <p/>
* Sending
* portA ---> portB
* <p/>
* Receiving
* portB ---> portA
* <p/>
* <i>Transmit and Receive are interdependents. To receive you MUST trasmit. </i>
*/
public class AudioChannel {
private MediaLocator locator;
private String localIpAddress;
private String ipAddress;
private int localPort;
private int portBase;
private Format format;
private Processor processor = null;
private RTPManager rtpMgrs[];
private DataSource dataOutput = null;
private AudioReceiver audioReceiver;
private List<SendStream> sendStreams = new ArrayList<SendStream>();
private boolean started = false;
/**
* Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://")
*
* @param locator
* @param ipAddress
* @param localPort
* @param remotePort
* @param format
*/
public AudioChannel(MediaLocator locator,
String localIpAddress,
String ipAddress,
int localPort,
int remotePort,
Format format) {
this.locator = locator;
this.localIpAddress = localIpAddress;
this.ipAddress = ipAddress;
this.localPort = localPort;
this.portBase = remotePort;
this.format = format;
}
/**
* Starts the transmission. Returns null if transmission started ok.
* Otherwise it returns a string with the reason why the setup failed.
* Starts receive also.
*/
public synchronized String start() {
if (started) return null;
started = true;
String result;
// Create a processor for the specified jmf locator
result = createProcessor();
if (result != null) {
started = false;
return result;
}
// Create an RTP session to transmit the output of the
// processor to the specified IP address and port no.
result = createTransmitter();
if (result != null) {
processor.close();
processor = null;
started = false;
return result;
}
// Start the transmission
processor.start();
return null;
}
/**
* Stops the transmission if already started.
* Stops the receiver also.
*/
public void stop() {
if (!started) return;
synchronized (this) {
try {
started = false;
if (processor != null) {
processor.stop();
processor = null;
for (int i = 0; i < rtpMgrs.length; i++) {
rtpMgrs[i].removeReceiveStreamListener(audioReceiver);
rtpMgrs[i].removeSessionListener(audioReceiver);
rtpMgrs[i].removeTargets("Session ended.");
rtpMgrs[i].dispose();
}
sendStreams.clear();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private String createProcessor() {
if (locator == null)
return "Locator is null";
DataSource ds;
try {
ds = javax.media.Manager.createDataSource(locator);
} catch (Exception e) {
e.printStackTrace();
return "Couldn't create DataSource";
}
// Try to create a processor to handle the input jmf locator
try {
processor = javax.media.Manager.createProcessor(ds);
} catch (NoProcessorException npe) {
npe.printStackTrace();
return "Couldn't create processor";
} catch (IOException ioe) {
ioe.printStackTrace();
return "IOException creating processor";
}
// Wait for it to configure
boolean result = waitForState(processor, Processor.Configured);
if (result == false)
return "Couldn't configure processor";
// Get the tracks from the processor
TrackControl[] tracks = processor.getTrackControls();
// Do we have atleast one track?
if (tracks == null || tracks.length < 1)
return "Couldn't find tracks in processor";
// Set the output content descriptor to RAW_RTP
// This will limit the supported formats reported from
// Track.getSupportedFormats to only valid RTP formats.
ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
processor.setContentDescriptor(cd);
Format supported[];
Format chosen = null;
boolean atLeastOneTrack = false;
// Program the tracks.
for (int i = 0; i < tracks.length; i++) {
if (tracks[i].isEnabled()) {
supported = tracks[i].getSupportedFormats();
if (supported.length > 0) {
for (Format format : supported) {
if (format instanceof AudioFormat) {
if (this.format.matches(format))
chosen = format;
}
}
if (chosen != null) {
tracks[i].setFormat(chosen);
System.err.println("Track " + i + " is set to transmit as:");
System.err.println(" " + chosen);
atLeastOneTrack = true;
} else
tracks[i].setEnabled(false);
} else
tracks[i].setEnabled(false);
}
}
if (!atLeastOneTrack)
return "Couldn't set any of the tracks to a valid RTP format";
result = waitForState(processor, Controller.Realized);
if (result == false)
return "Couldn't realize processor";
// Get the output data source of the processor
dataOutput = processor.getDataOutput();
return null;
}
/**
* Use the RTPManager API to create sessions for each jmf
* track of the processor.
*/
private String createTransmitter() {
// Cheated. Should have checked the type.
PushBufferDataSource pbds = (PushBufferDataSource) dataOutput;
PushBufferStream pbss[] = pbds.getStreams();
rtpMgrs = new RTPManager[pbss.length];
SessionAddress localAddr, destAddr;
InetAddress ipAddr;
SendStream sendStream;
audioReceiver = new AudioReceiver(this);
int port;
SourceDescription srcDesList[];
for (int i = 0; i < pbss.length; i++) {
try {
rtpMgrs[i] = RTPManager.newInstance();
port = portBase + 2 * i;
ipAddr = InetAddress.getByName(ipAddress);
localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress),
localPort);
destAddr = new SessionAddress(ipAddr, port);
rtpMgrs[i].addReceiveStreamListener(audioReceiver);
rtpMgrs[i].addSessionListener(audioReceiver);
rtpMgrs[i].initialize(localAddr);
rtpMgrs[i].addTarget(destAddr);
System.err.println("Created RTP session at " + localPort + " to: " + ipAddress + " " + port);
sendStream = rtpMgrs[i].createSendStream(dataOutput, i);
sendStreams.add(sendStream);
sendStream.start();
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
return null;
}
/**
* Set transmit activity. If the active is true, the instance should trasmit.
* If it is set to false, the instance should pause transmit.
*
* @param active
*/
public void setTrasmit(boolean active) {
for (SendStream sendStream : sendStreams) {
try {
if (active) {
sendStream.start();
System.out.println("START");
} else {
sendStream.stop();
System.out.println("STOP");
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* *************************************************************
* Convenience methods to handle processor's state changes.
* **************************************************************
*/
private Integer stateLock = new Integer(0);
private boolean failed = false;
Integer getStateLock() {
return stateLock;
}
void setFailed() {
failed = true;
}
private synchronized boolean waitForState(Processor p, int state) {
p.addControllerListener(new StateListener());
failed = false;
// Call the required method on the processor
if (state == Processor.Configured) {
p.configure();
} else if (state == Processor.Realized) {
p.realize();
}
// Wait until we get an event that confirms the
// success of the method, or a failure event.
// See StateListener inner class
while (p.getState() < state && !failed) {
synchronized (getStateLock()) {
try {
getStateLock().wait();
} catch (InterruptedException ie) {
return false;
}
}
}
if (failed)
return false;
else
return true;
}
/**
* *************************************************************
* Inner Classes
* **************************************************************
*/
class StateListener implements ControllerListener {
public void controllerUpdate(ControllerEvent ce) {
// If there was an error during configure or
// realize, the processor will be closed
if (ce instanceof ControllerClosedEvent)
setFailed();
// All controller events, send a notification
// to the waiting thread in waitForState method.
if (ce instanceof ControllerEvent) {
synchronized (getStateLock()) {
getStateLock().notifyAll();
}
}
}
}
public static void main(String args[]) {
InetAddress localhost;
try {
localhost = InetAddress.getLocalHost();
AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP));
AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP));
audioChannel0.start();
audioChannel1.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.setTrasmit(false);
audioChannel1.setTrasmit(false);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.setTrasmit(true);
audioChannel1.setTrasmit(true);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.stop();
audioChannel1.stop();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,51 @@
/**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.jingleaudio.jmf;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import javax.media.format.AudioFormat;
/**
* Audio Format Ttils.
*/
public class AudioFormatUtils {
/**
* Return a JMF AudioFormat for a given Jingle Payload type.
* Return null if the payload is not supported by this jmf API.
*
* @param payloadtype
* @return
*/
public static AudioFormat getAudioFormat(PayloadType payloadtype) {
switch (payloadtype.getId()) {
case 3:
return new AudioFormat(AudioFormat.GSM_RTP);
case 4:
return new AudioFormat(AudioFormat.G723_RTP);
default:
return null;
}
}
}

View file

@ -0,0 +1,155 @@
package org.jivesoftware.jingleaudio.jmf; /**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.jivesoftware.jingleaudio.jmf.AudioChannel;
import org.jivesoftware.jingleaudio.jmf.AudioFormatUtils;
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import javax.media.MediaLocator;
import javax.media.format.AudioFormat;
import java.io.IOException;
import java.net.ServerSocket;
/**
* This Class implements a complete JingleMediaSession.
* It sould be used to transmit and receive audio captured from the Mic.
* This Class should be automaticly controlled by JingleSession.
* But you could also use in any VOIP application.
* For better NAT Traversal support this implementation don´t support only receive or only transmit.
* To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
*/
public class AudioMediaSession extends JingleMediaSession {
private AudioFormat format;
private AudioChannel audioChannel;
/**
* Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
*
* @param payloadType Payload of the jmf
* @param remote The remote information. The candidate that the jmf will be sent to.
* @param local The local information. The candidate that will receive the jmf
*/
public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
final TransportCandidate local) {
super(payloadType, remote, local);
}
/**
* Initialize the Audio Channel to make it able to send and receive audio
*/
public void initialize() {
String ip;
String localIp;
int localPort;
int remotePort;
if (this.getLocal().getSymmetric() != null) {
ip = this.getLocal().getIp();
localIp = this.getLocal().getLocalIp();
localPort = getFreePort();
remotePort = this.getLocal().getSymmetric().getPort();
System.out.println(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort);
} else {
ip = this.getRemote().getIp();
localIp = this.getLocal().getLocalIp();
localPort = this.getLocal().getPort();
remotePort = this.getRemote().getPort();
}
audioChannel = new AudioChannel(new MediaLocator("dsound://"), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()));
}
/**
* Starts transmission and for NAT Traversal reasons start receiving also.
*/
public void startTrasmit() {
audioChannel.start();
}
/**
* Set transmit activity. If the active is true, the instance should trasmit.
* If it is set to false, the instance should pause transmit.
*
* @param active
*/
public void setTrasmit(boolean active) {
audioChannel.setTrasmit(active);
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void startReceive() {
// Do nothing
}
/**
* Stops transmission and for NAT Traversal reasons stop receiving also.
*/
public void stopTrasmit() {
audioChannel.stop();
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void stopReceive() {
// Do nothing
}
/**
* Obtain a free port we can use.
*
* @return A free port number.
*/
protected int getFreePort() {
ServerSocket ss;
int freePort = 0;
for (int i = 0; i < 10; i++) {
freePort = (int) (10000 + Math.round(Math.random() * 10000));
freePort = freePort % 2 == 0 ? freePort : freePort + 1;
try {
ss = new ServerSocket(freePort);
freePort = ss.getLocalPort();
ss.close();
return freePort;
}
catch (IOException e) {
e.printStackTrace();
}
}
try {
ss = new ServerSocket(0);
freePort = ss.getLocalPort();
ss.close();
}
catch (IOException e) {
e.printStackTrace();
}
return freePort;
}
}

View file

@ -0,0 +1,143 @@
package org.jivesoftware.jingleaudio.jmf; /**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.media.*;
import javax.media.protocol.DataSource;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
/**
* This class implements receive methods and listeners to be used in AudioChannel
*/
public class AudioReceiver implements ReceiveStreamListener, SessionListener,
ControllerListener {
boolean dataReceived = false;
Object dataSync;
public AudioReceiver(Object dataSync) {
this.dataSync = dataSync;
}
/**
* JingleSessionListener.
*/
public synchronized void update(SessionEvent evt) {
if (evt instanceof NewParticipantEvent) {
Participant p = ((NewParticipantEvent) evt).getParticipant();
System.err.println(" - A new participant had just joined: " + p.getCNAME());
}
}
/**
* ReceiveStreamListener
*/
public synchronized void update(ReceiveStreamEvent evt) {
RTPManager mgr = (RTPManager) evt.getSource();
Participant participant = evt.getParticipant(); // could be null.
ReceiveStream stream = evt.getReceiveStream(); // could be null.
if (evt instanceof RemotePayloadChangeEvent) {
System.err.println(" - Received an RTP PayloadChangeEvent.");
System.err.println("Sorry, cannot handle payload change.");
// System.exit(0);
} else if (evt instanceof NewReceiveStreamEvent) {
try {
stream = ((NewReceiveStreamEvent) evt).getReceiveStream();
DataSource ds = stream.getDataSource();
// Find out the formats.
RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
if (ctl != null) {
System.err.println(" - Recevied new RTP stream: " + ctl.getFormat());
} else
System.err.println(" - Recevied new RTP stream");
if (participant == null)
System.err.println(" The sender of this stream had yet to be identified.");
else {
System.err.println(" The stream comes from: " + participant.getCNAME());
}
// create a player by passing datasource to the Media Manager
Player p = javax.media.Manager.createPlayer(ds);
if (p == null)
return;
p.addControllerListener(this);
p.realize();
// Notify intialize() that a new stream had arrived.
synchronized (dataSync) {
dataReceived = true;
dataSync.notifyAll();
}
} catch (Exception e) {
System.err.println("NewReceiveStreamEvent exception " + e.getMessage());
return;
}
} else if (evt instanceof StreamMappedEvent) {
if (stream != null && stream.getDataSource() != null) {
DataSource ds = stream.getDataSource();
// Find out the formats.
RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
System.err.println(" - The previously unidentified stream ");
if (ctl != null)
System.err.println(" " + ctl.getFormat());
System.err.println(" had now been identified as sent by: " + participant.getCNAME());
}
} else if (evt instanceof ByeEvent) {
System.err.println(" - Got \"bye\" from: " + participant.getCNAME());
}
}
/**
* ControllerListener for the Players.
*/
public synchronized void controllerUpdate(ControllerEvent ce) {
Player p = (Player) ce.getSourceController();
if (p == null)
return;
// Get this when the internal players are realized.
if (ce instanceof RealizeCompleteEvent) {
p.start();
}
if (ce instanceof ControllerErrorEvent) {
p.removeControllerListener(this);
System.err.println("Receiver internal error: " + ce);
}
}
}

View file

@ -0,0 +1,119 @@
package org.jivesoftware.jingleaudio.jmf;
/**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import java.io.File;
import java.io.IOException;
/**
* Implements a jingleMediaManager using JMF based API.
* It supports GSM and G723 codecs.
* <i>This API only currently works on windows.</i>
*/
public class JmfMediaManager extends JingleMediaManager {
/**
* Creates a Media Manager instance
*/
public JmfMediaManager() {
setupPayloads();
setupJMF();
}
/**
* Returns a new jingleMediaSession
*
* @param payloadType
* @param remote
* @param local
* @return
*/
public JingleMediaSession createMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local) {
return new AudioMediaSession(payloadType, remote, local);
}
/**
* Setup API supported Payloads
*/
private void setupPayloads() {
this.addPayloadType(new PayloadType.Audio(3, "gsm"));
this.addPayloadType(new PayloadType.Audio(4, "g723"));
}
/**
* Runs JMFInit the first time the application is started so that capture
* devices are properly detected and initialized by JMF.
*/
public static void setupJMF() {
try {
// .jmf is the place where we store the jmf.properties file used
// by JMF. if the directory does not exist or it does not contain
// a jmf.properties file. or if the jmf.properties file has 0 length
// then this is the first time we're running and should continue to
// with JMFInit
String homeDir = System.getProperty("user.home");
File jmfDir = new File(homeDir, ".jmf");
String classpath = System.getProperty("java.class.path");
classpath += System.getProperty("path.separator")
+ jmfDir.getAbsolutePath();
System.setProperty("java.class.path", classpath);
if (!jmfDir.exists())
jmfDir.mkdir();
File jmfProperties = new File(jmfDir, "jmf.properties");
if (!jmfProperties.exists()) {
try {
jmfProperties.createNewFile();
}
catch (IOException ex) {
System.out.println("Failed to create jmf.properties");
ex.printStackTrace();
}
}
// if we're running on linux checkout that libjmutil.so is where it
// should be and put it there.
runLinuxPreInstall();
if (jmfProperties.length() == 0) {
//JMFInit init = new JMFInit(null);
//init.setVisible(false);
}
}
finally {
}
}
private static void runLinuxPreInstall() {
// @TODO Implement Linux Pre-Install
}
}

View file

@ -0,0 +1,209 @@
package org.jivesoftware.jingleaudio.jspeex;
import mil.jfcom.cie.media.session.MediaSession;
import mil.jfcom.cie.media.session.MediaSessionListener;
import mil.jfcom.cie.media.session.StreamPlayer;
import mil.jfcom.cie.media.srtp.packetizer.SpeexFormat;
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import javax.media.NoProcessorException;
import javax.media.format.UnsupportedFormatException;
import javax.media.rtp.rtcp.SenderReport;
import javax.media.rtp.rtcp.SourceDescription;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.GeneralSecurityException;
/**
* $RCSfile$
* $Revision: $
* $Date: 25/12/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class AudioMediaSession extends JingleMediaSession implements MediaSessionListener {
private MediaSession mediaSession;
/**
* Create session for test program.
*
* @param micOn microphone turned on
*/
public static MediaSession createSession(String localhost, int localPort, String remoteHost, int remotePort, MediaSessionListener eventHandler, int quality, boolean secure, boolean micOn) throws NoProcessorException, UnsupportedFormatException, IOException, GeneralSecurityException {
SpeexFormat.setFramesPerPacket(1);
/**
* The master key. Hardcoded for now.
*/
byte[] masterKey = new byte[]{(byte) 0xE1, (byte) 0xF9, 0x7A, 0x0D, 0x3E, 0x01, (byte) 0x8B, (byte) 0xE0, (byte) 0xD6, 0x4F, (byte) 0xA3, 0x2C, 0x06, (byte) 0xDE, 0x41, 0x39};
/**
* The master salt. Hardcoded for now.
*/
byte[] masterSalt = new byte[]{0x0E, (byte) 0xC6, 0x75, (byte) 0xAD, 0x49, (byte) 0x8A, (byte) 0xFE, (byte) 0xEB, (byte) 0xB6, (byte) 0x96, 0x0B, 0x3A, (byte) 0xAB, (byte) 0xE6};
DatagramSocket[] localPorts = MediaSession.getLocalPorts(InetAddress.getByName(localhost), localPort);
MediaSession session = MediaSession.createInstance(remoteHost, remotePort, localPorts, quality, secure, masterKey, masterSalt);
session.setListener(eventHandler);
session.setSourceDescription(new SourceDescription[]{new SourceDescription(SourceDescription.SOURCE_DESC_NAME, "Superman", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_EMAIL, "cdcie.tester@je.jfcom.mil", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_LOC, InetAddress.getByName(localhost) + " Port " + session.getLocalDataPort(), 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_TOOL, "JFCOM CDCIE Audio Chat", 1, false)});
return session;
}
/**
* Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
*
* @param payloadType Payload of the jmf
* @param remote The remote information. The candidate that the jmf will be sent to.
* @param local The local information. The candidate that will receive the jmf
*/
public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
final TransportCandidate local) {
super(payloadType, remote, local);
}
/**
* Initialize the Audio Channel to make it able to send and receive audio
*/
public void initialize() {
String ip;
String localIp;
int localPort;
int remotePort;
if (this.getLocal().getSymmetric() != null) {
ip = this.getLocal().getIp();
localIp = this.getLocal().getLocalIp();
localPort = getFreePort();
remotePort = this.getLocal().getSymmetric().getPort();
System.out.println(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort);
} else {
ip = this.getRemote().getIp();
localIp = this.getLocal().getLocalIp();
localPort = this.getLocal().getPort();
remotePort = this.getRemote().getPort();
}
try {
mediaSession = createSession(localIp, localPort, ip, remotePort, this, 2, false, true);
} catch (NoProcessorException e) {
e.printStackTrace();
} catch (UnsupportedFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
}
/**
* Starts transmission and for NAT Traversal reasons start receiving also.
*/
public void startTrasmit() {
try {
System.out.println("start");
mediaSession.start(true);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Set transmit activity. If the active is true, the instance should trasmit.
* If it is set to false, the instance should pause transmit.
*
* @param active
*/
public void setTrasmit(boolean active) {
// Do nothing
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void startReceive() {
// Do nothing
}
/**
* Stops transmission and for NAT Traversal reasons stop receiving also.
*/
public void stopTrasmit() {
// Do nothing
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void stopReceive() {
// Do nothing
}
public void newStreamIdentified(StreamPlayer streamPlayer) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void senderReportReceived(SenderReport report) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void streamClosed(StreamPlayer stream, boolean timeout) {
//To change body of implemented methods use File | Settings | File Templates.
}
/**
* Obtain a free port we can use.
*
* @return A free port number.
*/
protected int getFreePort() {
ServerSocket ss;
int freePort = 0;
for (int i = 0; i < 10; i++) {
freePort = (int) (10000 + Math.round(Math.random() * 10000));
freePort = freePort % 2 == 0 ? freePort : freePort + 1;
try {
ss = new ServerSocket(freePort);
freePort = ss.getLocalPort();
ss.close();
return freePort;
}
catch (IOException e) {
e.printStackTrace();
}
}
try {
ss = new ServerSocket(0);
freePort = ss.getLocalPort();
ss.close();
}
catch (IOException e) {
e.printStackTrace();
}
return freePort;
}
}

View file

@ -0,0 +1,101 @@
package org.jivesoftware.jingleaudio.jspeex;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import java.io.File;
import java.io.IOException;
/**
* $RCSfile$
* $Revision: $
* $Date: 25/12/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class SpeexMediaManager extends JingleMediaManager {
public SpeexMediaManager() {
setupPayloads();
setupJMF();
}
public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local) {
return new AudioMediaSession(payloadType, remote, local);
}
/**
* Setup API supported Payloads
*/
private void setupPayloads() {
this.addPayloadType(new PayloadType.Audio(15, "speex"));
}
/**
* Runs JMFInit the first time the application is started so that capture
* devices are properly detected and initialized by JMF.
*/
public static void setupJMF() {
try {
// .jmf is the place where we store the jmf.properties file used
// by JMF. if the directory does not exist or it does not contain
// a jmf.properties file. or if the jmf.properties file has 0 length
// then this is the first time we're running and should continue to
// with JMFInit
String homeDir = System.getProperty("user.home");
File jmfDir = new File(homeDir, ".jmf");
String classpath = System.getProperty("java.class.path");
classpath += System.getProperty("path.separator")
+ jmfDir.getAbsolutePath();
System.setProperty("java.class.path", classpath);
if (!jmfDir.exists())
jmfDir.mkdir();
File jmfProperties = new File(jmfDir, "jmf.properties");
if (!jmfProperties.exists()) {
try {
jmfProperties.createNewFile();
}
catch (IOException ex) {
System.out.println("Failed to create jmf.properties");
ex.printStackTrace();
}
}
// if we're running on linux checkout that libjmutil.so is where it
// should be and put it there.
runLinuxPreInstall();
if (jmfProperties.length() == 0) {
//JMFInit init = new JMFInit(null);
//init.setVisible(false);
}
}
finally {
}
}
private static void runLinuxPreInstall() {
// @TODO Implement Linux Pre-Install
}
}

View file

@ -0,0 +1,362 @@
import junit.framework.TestCase;
import org.jivesoftware.jingleaudio.jmf.AudioChannel;
import org.jivesoftware.jingleaudio.jmf.JmfMediaManager;
import org.jivesoftware.jingleaudio.jspeex.SpeexMediaManager;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
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.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.nat.BridgedTransportManager;
import org.jivesoftware.smackx.jingle.nat.ICETransportManager;
import org.jivesoftware.smackx.jingle.nat.STUNTransportManager;
import javax.media.MediaLocator;
import javax.media.format.AudioFormat;
import java.net.InetAddress;
/**
* $RCSfile$
* $Revision: $
* $Date: 09/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class JingleMediaTest extends TestCase {
public void testCompleteJmf() {
try {
//XMPPConnection.DEBUG_ENABLED = true;
XMPPConnection x0 = new XMPPConnection("thiago");
XMPPConnection x1 = new XMPPConnection("thiago");
x0.connect();
x0.login("barata7", "barata7");
x1.connect();
x1.login("barata6", "barata6");
final JingleManager jm0 = new JingleManager(
x0, new ICETransportManager());
final JingleManager jm1 = new JingleManager(
x1, new ICETransportManager());
JingleMediaManager jingleMediaManager0 = new JmfMediaManager();
JingleMediaManager jingleMediaManager1 = new JmfMediaManager();
jm0.setMediaManager(jingleMediaManager0);
jm1.setMediaManager(jingleMediaManager1);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
IncomingJingleSession session = request.accept(jm1.getMediaManager().getPayloads());
session.start(request);
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
OutgoingJingleSession js0 = jm0.createOutgoingJingleSession("barata6@thiago/Smack");
js0.start();
Thread.sleep(50000);
js0.terminate();
Thread.sleep(6000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void testCompleteSpeex() {
try {
//XMPPConnection.DEBUG_ENABLED = true;
XMPPConnection x0 = new XMPPConnection("thiago");
XMPPConnection x1 = new XMPPConnection("thiago");
x0.connect();
x0.login("barata7", "barata7");
x1.connect();
x1.login("barata6", "barata6");
final JingleManager jm0 = new JingleManager(
x0, new STUNTransportManager());
final JingleManager jm1 = new JingleManager(
x1, new STUNTransportManager());
JingleMediaManager jingleMediaManager0 = new SpeexMediaManager();
JingleMediaManager jingleMediaManager1 = new SpeexMediaManager();
jm0.setMediaManager(jingleMediaManager0);
jm1.setMediaManager(jingleMediaManager1);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
IncomingJingleSession session = request.accept(jm1.getMediaManager().getPayloads());
session.start(request);
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
OutgoingJingleSession js0 = jm0.createOutgoingJingleSession("barata6@thiago/Smack");
js0.start();
Thread.sleep(150000);
js0.terminate();
Thread.sleep(6000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void testCompleteWithBridge() {
for (int i = 0; i < 1; i += 2) {
final int n = i;
Thread t = new Thread(new Runnable() {
public void run() {
try {
XMPPConnection x0 = new XMPPConnection("thiago");
XMPPConnection x1 = new XMPPConnection("thiago");
x0.connect();
x0.login("user" + String.valueOf(n), "user" + String.valueOf(n));
x1.connect();
x1.login("user" + String.valueOf(n + 1), "user" + String.valueOf(n + 1));
BridgedTransportManager btm0 = new BridgedTransportManager(x0);
BridgedTransportManager btm1 = new BridgedTransportManager(x1);
final JingleManager jm0 = new JingleManager(x0, btm0);
final JingleManager jm1 = new JingleManager(x1, btm1);
jm0.addCreationListener(btm0);
jm1.addCreationListener(btm1);
JingleMediaManager jingleMediaManager = new SpeexMediaManager();
JingleMediaManager jingleMediaManager2 = new SpeexMediaManager();
jm0.setMediaManager(jingleMediaManager);
jm1.setMediaManager(jingleMediaManager2);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
IncomingJingleSession session = request.accept(jm1.getMediaManager().getPayloads());
session.start(request);
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
OutgoingJingleSession js0 = jm0.createOutgoingJingleSession("user" + String.valueOf(n + 1) + "@thiago/Smack");
js0.start();
Thread.sleep(55000);
js0.terminate();
Thread.sleep(3000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.start();
}
try {
Thread.sleep(250000);
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
public void testCompleteWithBridgeB() {
try {
//XMPPConnection.DEBUG_ENABLED = true;
XMPPConnection x0 = new XMPPConnection("thiago");
XMPPConnection x1 = new XMPPConnection("thiago");
x0.connect();
x0.login("barata5", "barata5");
x1.connect();
x1.login("barata4", "barata4");
BridgedTransportManager btm0 = new BridgedTransportManager(x0);
BridgedTransportManager btm1 = new BridgedTransportManager(x1);
final JingleManager jm0 = new JingleManager(x0, btm0);
final JingleManager jm1 = new JingleManager(x1, btm1);
jm0.addCreationListener(btm0);
jm1.addCreationListener(btm1);
JingleMediaManager jingleMediaManager = new JmfMediaManager();
jm0.setMediaManager(jingleMediaManager);
jm1.setMediaManager(jingleMediaManager);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
IncomingJingleSession session = request.accept(jm1.getMediaManager().getPayloads());
session.start(request);
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
OutgoingJingleSession js0 = jm0.createOutgoingJingleSession("barata4@thiago/Smack");
js0.start();
Thread.sleep(20000);
js0.terminate();
Thread.sleep(3000);
js0 = jm0.createOutgoingJingleSession("barata4@thiago/Smack");
js0.start();
Thread.sleep(20000);
js0.terminate();
Thread.sleep(3000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void testAudioChannelOpenClose
() {
for (int i = 0; i < 5; i++) {
try {
AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost().getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP));
AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost().getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP));
audioChannel0.start();
audioChannel1.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.stop();
audioChannel1.stop();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void testAudioChannelStartStop
() {
try {
AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost().getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP));
AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost().getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP));
for (int i = 0; i < 5; i++) {
audioChannel0.start();
audioChannel1.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.stop();
audioChannel1.stop();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}