mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-27 00:32:07 +01:00
Jingle Extension and Media Merged
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@7425 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
6bb444fdaf
commit
9feb6ece42
11 changed files with 1821 additions and 1 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,454 @@
|
||||||
|
/**
|
||||||
|
* $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.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.
|
||||||
|
* <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>
|
||||||
|
*
|
||||||
|
* @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<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 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* $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.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/**
|
||||||
|
* $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.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* $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.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/**
|
||||||
|
* $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.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.
|
||||||
|
* <i>This API only currently works on windows and Mac.</i>
|
||||||
|
*
|
||||||
|
* @author Thiago Camargo
|
||||||
|
*/
|
||||||
|
public class JmfMediaManager extends JingleMediaManager {
|
||||||
|
|
||||||
|
private List<PayloadType> payloads = new ArrayList<PayloadType>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<PayloadType> 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision: $
|
||||||
|
* $Date: 25/12/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.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision: $
|
||||||
|
* $Date: 25/12/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.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.
|
||||||
|
* <i>This API only currently works on windows.</i>
|
||||||
|
*
|
||||||
|
* @author Thiago Camargo
|
||||||
|
*/
|
||||||
|
public class SpeexMediaManager extends JingleMediaManager {
|
||||||
|
|
||||||
|
private List<PayloadType> payloads = new ArrayList<PayloadType>();
|
||||||
|
|
||||||
|
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<PayloadType> 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision: $
|
||||||
|
* $Date: 25/12/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.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<JingleMediaManager> managers = new ArrayList<JingleMediaManager>();
|
||||||
|
|
||||||
|
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<PayloadType> getPayloads() {
|
||||||
|
List<PayloadType> list = new ArrayList<PayloadType>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,9 +66,9 @@ import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
|
||||||
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
|
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
|
||||||
import org.jivesoftware.smackx.jingle.media.PayloadType;
|
import org.jivesoftware.smackx.jingle.media.PayloadType;
|
||||||
import org.jivesoftware.smackx.jingle.nat.*;
|
import org.jivesoftware.smackx.jingle.nat.*;
|
||||||
|
import org.jivesoftware.smackx.jingle.mediaimpl.jmf.JmfMediaManager;
|
||||||
import org.jivesoftware.smackx.packet.Jingle;
|
import org.jivesoftware.smackx.packet.Jingle;
|
||||||
import org.jivesoftware.smackx.provider.JingleProvider;
|
import org.jivesoftware.smackx.provider.JingleProvider;
|
||||||
import org.jivesoftware.jingleaudio.jmf.JmfMediaManager;
|
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
|
|
Loading…
Reference in a new issue