diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java
new file mode 100644
index 000000000..bdc3545fa
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java
@@ -0,0 +1,275 @@
+package org.jivesoftware.smackx.jingle.mediaimpl;
+
+import com.sun.media.util.Registry;
+import com.sun.media.ExclusiveUse;
+
+import javax.media.format.AudioFormat;
+import javax.media.Renderer;
+import javax.media.PlugInManager;
+import javax.media.Format;
+import java.awt.*;
+import java.util.Vector;
+
+public class JMFInit extends Frame implements Runnable {
+
+ private String tempDir = "/tmp";
+
+ private boolean done = false;
+
+ private String userHome;
+
+ private boolean visible = false;
+
+ public JMFInit(String[] args, boolean visible) {
+ super("Initializing JMF...");
+
+ this.visible = visible;
+
+ Registry.set("secure.allowCaptureFromApplets", true);
+ Registry.set("secure.allowSaveFileFromApplets", true);
+
+ updateTemp(args);
+
+ try {
+ Registry.commit();
+ }
+ catch (Exception e) {
+
+ message("Failed to commit to JMFRegistry!");
+ }
+
+ Thread detectThread = new Thread(this);
+ detectThread.run();
+
+ /*
+ * int slept = 0; while (!done && slept < 60 * 1000 * 2) { try {
+ * Thread.currentThread().sleep(500); } catch (InterruptedException ie) { }
+ * slept += 500; }
+ *
+ * if (!done) { console.error("Detection is taking too long!
+ * Aborting!"); message("Detection is taking too long! Aborting!"); }
+ *
+ * try { Thread.currentThread().sleep(2000); } catch
+ * (InterruptedException ie) { }
+ */
+ }
+
+ public void run() {
+ detectDirectAudio();
+ detectS8DirectAudio();
+ detectCaptureDevices();
+ done = true;
+ }
+
+ private void updateTemp(String[] args) {
+ if (args != null && args.length > 0) {
+ tempDir = args[0];
+
+ message("Setting cache directory to " + tempDir);
+ Registry r = new Registry();
+ try {
+ r.set("secure.cacheDir", tempDir);
+ r.commit();
+
+ message("Updated registry");
+ }
+ catch (Exception e) {
+ message("Couldn't update registry!");
+ }
+ }
+ }
+
+ private void detectCaptureDevices() {
+ // check if JavaSound capture is available
+ message("Looking for Audio capturer");
+ Class dsauto;
+ try {
+ dsauto = Class.forName("DirectSoundAuto");
+ dsauto.newInstance();
+ message("Finished detecting DirectSound capturer");
+ }
+ catch (ThreadDeath td) {
+ throw td;
+ }
+ catch (Throwable t) {
+ //Do nothing
+ }
+
+ Class jsauto;
+ try {
+ jsauto = Class.forName("JavaSoundAuto");
+ jsauto.newInstance();
+ message("Finished detecting javasound capturer");
+ }
+ catch (ThreadDeath td) {
+ throw td;
+ }
+ catch (Throwable t) {
+ message("JavaSound capturer detection failed!");
+ }
+
+ /*
+ // Check if VFWAuto or SunVideoAuto is available
+ message("Looking for video capture devices");
+ Class auto = null;
+ Class autoPlus = null;
+ try {
+ auto = Class.forName("VFWAuto");
+ }
+ catch (Exception e) {
+ }
+ if (auto == null) {
+ try {
+ auto = Class.forName("SunVideoAuto");
+ }
+ catch (Exception ee) {
+
+ }
+ try {
+ autoPlus = Class.forName("SunVideoPlusAuto");
+ }
+ catch (Exception ee) {
+
+ }
+ }
+ if (auto == null) {
+ try {
+ auto = Class.forName("V4LAuto");
+ }
+ catch (Exception ee) {
+
+ }
+ }
+ try {
+ Object instance = auto.newInstance();
+ if (autoPlus != null) {
+ Object instancePlus = autoPlus.newInstance();
+ }
+
+ message("Finished detecting video capture devices");
+ }
+ catch (ThreadDeath td) {
+ throw td;
+ }
+ catch (Throwable t) {
+
+ message("Capture device detection failed!");
+ }
+ */
+ }
+
+ private void detectDirectAudio() {
+ Class cls;
+ int plType = PlugInManager.RENDERER;
+ String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
+ try {
+ // Check if this is the Windows Performance Pack - hack
+ cls = Class.forName("VFWAuto");
+ // Check if DS capture is supported, otherwise fail DS renderer
+ // since NT doesn't have capture
+ cls = Class.forName("com.sun.media.protocol.dsound.DSound");
+ // Find the renderer class and instantiate it.
+ cls = Class.forName(dar);
+
+ Renderer rend = (Renderer) cls.newInstance();
+ try {
+ // Set the format and open the device
+ AudioFormat af = new AudioFormat(AudioFormat.LINEAR, 44100, 16,
+ 2);
+ rend.setInputFormat(af);
+ rend.open();
+ Format[] inputFormats = rend.getSupportedInputFormats();
+ // Register the device
+ PlugInManager.addPlugIn(dar, inputFormats, new Format[0],
+ plType);
+ // Move it to the top of the list
+ Vector rendList = PlugInManager.getPlugInList(null, null,
+ plType);
+ int listSize = rendList.size();
+ if (rendList.elementAt(listSize - 1).equals(dar)) {
+ rendList.removeElementAt(listSize - 1);
+ rendList.insertElementAt(dar, 0);
+ PlugInManager.setPlugInList(rendList, plType);
+ PlugInManager.commit();
+ // Log.debug("registered");
+ }
+ rend.close();
+ }
+ catch (Throwable t) {
+ // Log.debug("Error " + t);
+ }
+ }
+ catch (Throwable tt) {
+ //Do nothing
+ }
+ }
+
+ private void detectS8DirectAudio() {
+ Class cls;
+ int plType = PlugInManager.RENDERER;
+ String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
+ try {
+ // Check if this is the solaris Performance Pack - hack
+ cls = Class.forName("SunVideoAuto");
+
+ // Find the renderer class and instantiate it.
+ cls = Class.forName(dar);
+
+ Renderer rend = (Renderer) cls.newInstance();
+
+ if (rend instanceof ExclusiveUse
+ && !((ExclusiveUse) rend).isExclusive()) {
+ // sol8+, DAR supports mixing
+ Vector rendList = PlugInManager.getPlugInList(null, null,
+ plType);
+ int listSize = rendList.size();
+ boolean found = false;
+ String rname = null;
+
+ for (int i = 0; i < listSize; i++) {
+ rname = (String) (rendList.elementAt(i));
+ if (rname.equals(dar)) { // DAR is in the registry
+ found = true;
+ rendList.removeElementAt(i);
+ break;
+ }
+ }
+
+ if (found) {
+ rendList.insertElementAt(dar, 0);
+ PlugInManager.setPlugInList(rendList, plType);
+ PlugInManager.commit();
+ }
+ }
+ }
+ catch (Throwable tt) {
+ //Do nothing
+ }
+ }
+
+ private void message(String mesg) {
+ System.out.println(mesg);
+ }
+
+ private void createGUI() {
+ TextArea textBox = new TextArea(5, 50);
+ add("Center", textBox);
+ textBox.setEditable(false);
+ addNotify();
+ pack();
+
+ int scrWidth = (int) Toolkit.getDefaultToolkit().getScreenSize()
+ .getWidth();
+ int scrHeight = (int) Toolkit.getDefaultToolkit().getScreenSize()
+ .getHeight();
+
+ setLocation((scrWidth - getWidth()) / 2, (scrHeight - getHeight()) / 2);
+
+ setVisible(visible);
+
+ }
+
+ public static void start(boolean visible) {
+ new JMFInit(null, visible);
+ }
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java
new file mode 100644
index 000000000..1ee9ec196
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java
@@ -0,0 +1,156 @@
+package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+
+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);
+ }
+
+ }
+
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java
new file mode 100644
index 000000000..96b03d4a1
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java
@@ -0,0 +1,454 @@
+/**
+ * $RCSfile$
+ * $Revision: $
+ * $Date: 08/11/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.
+ */
+package org.jivesoftware.smackx.jingle.mediaimpl.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 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.
+ *
+ * Send from portA to portB and receive from portB in portA.
+ *
+ * Sending
+ * portA ---> portB
+ *
+ * Receiving
+ * portB ---> portA
+ *
+ * Transmit and Receive are interdependents. To receive you MUST trasmit.
+ *
+ * @author Thiago Camargo
+ */
+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 sendStreams = new ArrayList();
+
+ private boolean started = false;
+
+ /**
+ * Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://")
+ *
+ * @param locator media locator
+ * @param localIpAddress local IP address
+ * @param ipAddress 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,
+ 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.
+ *
+ * @return result description
+ */
+ 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 (RTPManager rtpMgr : rtpMgrs) {
+ rtpMgr.removeReceiveStreamListener(audioReceiver);
+ rtpMgr.removeSessionListener(audioReceiver);
+ rtpMgr.removeTargets("Session ended.");
+ rtpMgr.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)
+ 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)
+ 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.
+ *
+ * @return description
+ */
+ 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;
+
+ 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 active state
+ */
+ 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 = 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;
+ }
+ }
+ }
+
+ return !failed;
+ }
+
+ /**
+ * *************************************************************
+ * 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 != null) {
+ 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();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java
new file mode 100644
index 000000000..ae5e3541d
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java
@@ -0,0 +1,55 @@
+/**
+ * $RCSfile$
+ * $Revision: $
+ * $Date: 08/11/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.
+ */
+package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+
+import org.jivesoftware.smackx.jingle.media.PayloadType;
+
+import javax.media.format.AudioFormat;
+
+/**
+ * Audio Format Utils.
+ *
+ * @author Thiago Camargo
+ */
+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 payloadtype
+ * @return correspondent audioType
+ */
+ public static AudioFormat getAudioFormat(PayloadType payloadtype) {
+
+ switch (payloadtype.getId()) {
+ case 0:
+ return new AudioFormat(AudioFormat.ULAW_RTP);
+ case 3:
+ return new AudioFormat(AudioFormat.GSM_RTP);
+ case 4:
+ return new AudioFormat(AudioFormat.G723_RTP);
+ default:
+ return null;
+ }
+
+ }
+
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java
new file mode 100644
index 000000000..4a1abce84
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java
@@ -0,0 +1,174 @@
+/**
+ * $RCSfile$
+ * $Revision: $
+ * $Date: 08/11/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.
+ */
+
+package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+
+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 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()
+ *
+ * @author Thiago Camargo
+ */
+public class AudioMediaSession extends JingleMediaSession {
+
+ private AudioChannel audioChannel;
+ private String locator = "javasound://";
+
+ /**
+ * 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) {
+ this(payloadType, remote, local, "javasound://");
+ }
+
+ /**
+ * 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
+ * @param locator media locator
+ */
+ public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
+ final TransportCandidate local, String locator) {
+ super(payloadType, remote, local);
+ if (locator != null && !locator.equals(""))
+ this.locator = locator;
+ initialize();
+ }
+
+ /**
+ * 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(locator), 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 active state
+ */
+ 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() {
+ if (audioChannel != null)
+ 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;
+ }
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java
new file mode 100644
index 000000000..72c0c2ce7
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java
@@ -0,0 +1,144 @@
+/**
+ * $RCSfile$
+ * $Revision: $
+ * $Date: 08/11/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.
+ */
+
+package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+
+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
+ *
+ * @author Thiago Camargo
+ */
+public class AudioReceiver implements ReceiveStreamListener, SessionListener,
+ ControllerListener {
+
+ boolean dataReceived = false;
+
+ Object dataSync;
+
+ public AudioReceiver(final 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) {
+
+ 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.");
+
+ } else if (evt instanceof NewReceiveStreamEvent) {
+
+ try {
+ stream = 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);
+ }
+
+ }
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java
new file mode 100644
index 000000000..5ac6c91fe
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java
@@ -0,0 +1,127 @@
+/**
+ * $RCSfile$
+ * $Revision: $
+ * $Date: 08/11/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.
+ */
+
+package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+
+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 org.jivesoftware.smackx.jingle.mediaimpl.JMFInit;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Implements a jingleMediaManager using JMF based API.
+ * It supports GSM and G723 codecs.
+ * This API only currently works on windows and Mac.
+ *
+ * @author Thiago Camargo
+ */
+public class JmfMediaManager extends JingleMediaManager {
+
+ private List payloads = new ArrayList();
+
+ /**
+ * Creates a Media Manager instance
+ */
+ public JmfMediaManager() {
+ setupPayloads();
+ }
+
+ /**
+ * Returns a new jingleMediaSession
+ *
+ * @param payloadType payloadType
+ * @param remote remote Candidate
+ * @param local local Candidate
+ * @return JingleMediaSession
+ */
+ 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() {
+ payloads.add(new PayloadType.Audio(3, "gsm"));
+ payloads.add(new PayloadType.Audio(4, "g723"));
+ payloads.add(new PayloadType.Audio(0, "PCMU", 16000));
+ }
+
+ /**
+ * Return all supported Payloads for this Manager
+ *
+ * @return The Payload List
+ */
+ public List getPayloads() {
+ return payloads;
+ }
+
+ /**
+ * Runs JMFInit the first time the application is started so that capture
+ * devices are properly detected and initialized by JMF.
+ */
+ public static void setupJMF() {
+ // .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) {
+ new JMFInit(null, false);
+ //}
+
+ }
+
+ private static void runLinuxPreInstall() {
+ // @TODO Implement Linux Pre-Install
+ }
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java
new file mode 100644
index 000000000..0fa5aba2c
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java
@@ -0,0 +1,237 @@
+/**
+ * $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.
+ */
+
+package org.jivesoftware.smackx.jingle.mediaimpl.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;
+
+/**
+ * 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()
+ *
+ * @author Thiago Camargo
+ */
+
+public class AudioMediaSession extends JingleMediaSession implements MediaSessionListener {
+
+ private MediaSession mediaSession;
+
+ /**
+ * Create a Session using Speex Codec
+ *
+ * @param localhost localHost
+ * @param localPort localPort
+ * @param remoteHost remoteHost
+ * @param remotePort remotePort
+ * @param eventHandler eventHandler
+ * @param quality quality
+ * @param secure secure
+ * @param micOn micOn
+ * @return MediaSession
+ * @throws NoProcessorException
+ * @throws UnsupportedFormatException
+ * @throws IOException
+ * @throws GeneralSecurityException
+ */
+ 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();
+ }
+
+ /**
+ * 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 active state
+ */
+ 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() {
+ if (mediaSession != null)
+ mediaSession.close();
+ }
+
+ /**
+ * 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) {
+ }
+
+ public void senderReportReceived(SenderReport report) {
+ }
+
+ public void streamClosed(StreamPlayer stream, boolean timeout) {
+ }
+
+ /**
+ * 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;
+ }
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java
new file mode 100644
index 000000000..bf6aae6d0
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java
@@ -0,0 +1,114 @@
+/**
+ * $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.
+ */
+package org.jivesoftware.smackx.jingle.mediaimpl.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 org.jivesoftware.smackx.jingle.mediaimpl.JMFInit;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Implements a jingleMediaManager using JMF based API and JSpeex.
+ * It supports Speex codec.
+ * This API only currently works on windows.
+ *
+ * @author Thiago Camargo
+ */
+public class SpeexMediaManager extends JingleMediaManager {
+
+ private List payloads = new ArrayList();
+
+ 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() {
+ payloads.add(new PayloadType.Audio(15, "speex"));
+ }
+
+ /**
+ * Return all supported Payloads for this Manager
+ *
+ * @return The Payload List
+ */
+ public List getPayloads() {
+ return payloads;
+ }
+
+ /**
+ * Runs JMFInit the first time the application is started so that capture
+ * devices are properly detected and initialized by JMF.
+ */
+ public static void setupJMF() {
+ // .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) {
+ new JMFInit(null, false);
+ }
+
+ }
+
+ private static void runLinuxPreInstall() {
+ // @TODO Implement Linux Pre-Install
+ }
+}
diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java
new file mode 100644
index 000000000..929306cc1
--- /dev/null
+++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java
@@ -0,0 +1,84 @@
+/**
+ * $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.
+ */
+
+package org.jivesoftware.smackx.jingle.mediaimpl.multi;
+
+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.util.*;
+
+/**
+ * Implements a MultiMediaManager using other JingleMediaManager implementations.
+ * It supports every Codecs that JingleMediaManagers added has.
+ *
+ * @author Thiago Camargo
+ */
+
+public class MultiMediaManager extends JingleMediaManager {
+
+ private List managers = new ArrayList();
+
+ public MultiMediaManager() {
+ }
+
+ public void addMediaManager(JingleMediaManager manager) {
+ managers.add(manager);
+ }
+
+ public void removeMediaManager(JingleMediaManager manager) {
+ managers.remove(manager);
+ }
+
+ /**
+ * Return all supported Payloads for this Manager.
+ *
+ * @return The Payload List
+ */
+ public List getPayloads() {
+ List list = new ArrayList();
+ for (JingleMediaManager manager : managers) {
+ for (PayloadType payloadType : manager.getPayloads()) {
+ if (!list.contains(payloadType))
+ list.add(payloadType);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Returns a new JingleMediaSession
+ *
+ * @param payloadType payloadType
+ * @param remote remote Candidate
+ * @param local local Candidate
+ * @return JingleMediaSession JingleMediaSession
+ */
+ public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local) {
+ for (JingleMediaManager manager : managers) {
+ if (manager.getPayloads().contains(payloadType)) {
+ return manager.createMediaSession(payloadType, remote, local);
+ }
+ }
+ return null;
+ }
+}
diff --git a/jingle/extension/test/org/jivesoftware/smackx/jingle/JingleManagerTest.java b/jingle/extension/test/org/jivesoftware/smackx/jingle/JingleManagerTest.java
index 0ca56dd4f..02d7c7f95 100644
--- a/jingle/extension/test/org/jivesoftware/smackx/jingle/JingleManagerTest.java
+++ b/jingle/extension/test/org/jivesoftware/smackx/jingle/JingleManagerTest.java
@@ -66,9 +66,9 @@ 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.*;
+import org.jivesoftware.smackx.jingle.mediaimpl.jmf.JmfMediaManager;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.provider.JingleProvider;
-import org.jivesoftware.jingleaudio.jmf.JmfMediaManager;
import java.net.DatagramPacket;
import java.net.DatagramSocket;