1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-09 18:29:45 +02:00

Renamed smack-jingle to smack-jingle-old

Also renamed package name from

org.jivesoftware.smackx.jingle

to

org.jivesoftware.smackx.jingleold
This commit is contained in:
Florian Schmaus 2014-08-20 00:25:57 +02:00
parent 6ab2bf9fe0
commit f65c0d5528
128 changed files with 300 additions and 300 deletions

View file

@ -0,0 +1,11 @@
description = """\
Smack Jingle API.
Warning: This API is beta, outdated and currenlty unmaintained."""
dependencies {
compile project(':smack-core')
compile project(':smack-extensions')
compile 'javax.media:jmf:2.1.1e'
compile files('libs/jspeex-0.9.7-jfcom.jar', 'libs/jstun.jar', 'libs/Speex.jar')
testCompile project(':smack-core').sourceSets.test.runtimeClasspath
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,176 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.demo;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.TCPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager;
import org.jivesoftware.smackx.jingle.nat.ICETransportManager;
import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
/**
* Jingle Demo Application. It register in a XMPP Server and let users place calls using a full JID and auto-receive calls.
* Parameters: Server User Pass.
*/
public class Demo extends JFrame {
private static final long serialVersionUID = -6584021277434403855L;
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 JingleSession incoming = null;
private JingleSession outgoing = null;
private JTextField jid;
public Demo(String server, String user, String pass) {
this.server = server;
this.user = user;
this.pass = pass;
if (user.equals("jeffw")) {
jid = new JTextField("eowyn" + "@" + server + "/Smack");
} else {
jid = new JTextField("jeffw" + "@" + server + "/Smack");
}
xmppConnection = new XMPPTCPConnection(server);
try {
xmppConnection.connect();
xmppConnection.login(user, pass);
initialize();
}
catch (XMPPException e) {
e.printStackTrace();
}
}
public void initialize() {
ICETransportManager icetm0 = new ICETransportManager(xmppConnection, "10.47.47.53", 3478);
List<JingleMediaManager> mediaManagers = new ArrayList<JingleMediaManager>();
//mediaManagers.add(new JmfMediaManager(icetm0));
mediaManagers.add(new SpeexMediaManager(icetm0));
mediaManagers.add(new ScreenShareMediaManager(icetm0));
jm = new JingleManager(xmppConnection, mediaManagers);
jm.addCreationListener(icetm0);
jm.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(JingleSessionRequest request) {
// if (incoming != null)
// return;
try {
// Accept the call
incoming = request.accept();
// Start the call
incoming.startIncoming();
}
catch (XMPPException e) {
e.printStackTrace();
}
}
});
createGUI();
}
public void createGUI() {
JPanel jPanel = new JPanel();
jPanel.add(jid);
jPanel.add(new JButton(new AbstractAction("Call") {
private static final long serialVersionUID = 4308448034795312815L;
public void actionPerformed(ActionEvent e) {
if (outgoing != null) return;
try {
outgoing = jm.createOutgoingJingleSession(jid.getText());
outgoing.startOutgoing();
}
catch (XMPPException e1) {
e1.printStackTrace();
}
}
}));
jPanel.add(new JButton(new AbstractAction("Hangup") {
private static final long serialVersionUID = -4508007389146723587L;
public void actionPerformed(ActionEvent e) {
if (outgoing != null)
try {
outgoing.terminate();
}
catch (XMPPException e1) {
e1.printStackTrace();
}
finally {
outgoing = null;
}
if (incoming != null)
try {
incoming.terminate();
}
catch (XMPPException e1) {
e1.printStackTrace();
}
finally {
incoming = null;
}
}
}));
this.add(jPanel);
}
public static void main(String args[]) {
Demo demo = null;
if (args.length > 2) {
demo = new Demo(args[0], args[1], args[2]);
demo.pack();
demo.setVisible(true);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
}

View file

@ -0,0 +1,538 @@
package org.jivesoftware.smackx.jingle;
/**
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.jivesoftware.smack.TCPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.jmf.AudioChannel;
import org.jivesoftware.smackx.jingle.mediaimpl.jmf.JmfMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.multi.MultiMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager;
import org.jivesoftware.smackx.jingle.nat.BridgedTransportManager;
import org.jivesoftware.smackx.jingle.nat.ICETransportManager;
import org.jivesoftware.smackx.jingle.nat.STUNTransportManager;
import org.jivesoftware.smackx.jingle.packet.JingleError;
import javax.media.MediaLocator;
import javax.media.format.AudioFormat;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
/**
* Test the Jingle Media using the high level API
* </p>
*
* @author Thiago Camargo
*/
public class JingleMediaTest extends SmackTestCase {
public JingleMediaTest(final String name) {
super(name);
}
public void testCompleteJmf() {
XMPPTCPConnection x0 = getConnection(0);
XMPPTCPConnection x1 = getConnection(1);
for (int i = 0; i < 1; i++)
try {
ICETransportManager icetm0 = new ICETransportManager(x0, "jivesoftware.com", 3478);
ICETransportManager icetm1 = new ICETransportManager(x1, "jivesoftware.com", 3478);
JingleMediaManager jingleMediaManager0 = new JmfMediaManager(icetm0);
JingleMediaManager jingleMediaManager1 = new JmfMediaManager(icetm1);
List<JingleMediaManager> jml0 = new ArrayList<JingleMediaManager>();
List<JingleMediaManager> jml1 = new ArrayList<JingleMediaManager>();
jml0.add(jingleMediaManager0);
jml1.add(jingleMediaManager1);
final JingleManager jm0 = new JingleManager(x0, jml0);
final JingleManager jm1 = new JingleManager(x1, jml1);
jm0.addCreationListener(icetm0);
jm1.addCreationListener(icetm1);
JingleSessionRequestListener jingleSessionRequestListener = new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
JingleSession session = request.accept();
session.startIncoming();
// session.addStateListener(new JingleSessionStateListener() {
// public void beforeChange(JingleNegotiator.State old, JingleNegotiator.State newOne)
// throws JingleNegotiator.JingleException {
// if (newOne instanceof IncomingJingleSession.Active) {
// throw new JingleNegotiator.JingleException();
// }
// }
//
// public void afterChanged(JingleNegotiator.State old, JingleNegotiator.State newOne) {
//
// }
// });
} catch (XMPPException e) {
e.printStackTrace();
}
}
};
jm1.addJingleSessionRequestListener(jingleSessionRequestListener);
JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser());
js0.startOutgoing();
Thread.sleep(20000);
JingleSession incomingJingleSession = jm1.getSession(js0.getConnection().getUser());
//JingleSession.removeAllStateListeners();
Thread.sleep(15000);
js0.terminate();
jm1.removeJingleSessionRequestListener(jingleSessionRequestListener);
Thread.sleep(60000);
} catch (Exception e) {
e.printStackTrace();
}
}
public void testCompleteMulti() {
try {
XMPPTCPConnection x0 = getConnection(0);
XMPPTCPConnection x1 = getConnection(1);
ICETransportManager icetm0 = new ICETransportManager(x0, "jivesoftware.com", 3478);
ICETransportManager icetm1 = new ICETransportManager(x1, "jivesoftware.com", 3478);
MultiMediaManager jingleMediaManager0 = new MultiMediaManager(icetm0);
jingleMediaManager0.addMediaManager(new JmfMediaManager(icetm0));
jingleMediaManager0.addMediaManager(new SpeexMediaManager(icetm0));
jingleMediaManager0.setPreferredPayloadType(jingleMediaManager0.getPayloads().get(1));
List<JingleMediaManager> jml0 = new ArrayList<JingleMediaManager>();
jml0.add(jingleMediaManager0);
MultiMediaManager jingleMediaManager1 = new MultiMediaManager(icetm1);
jingleMediaManager1.addMediaManager(new JmfMediaManager(icetm1));
jingleMediaManager1.addMediaManager(new SpeexMediaManager(icetm1));
jingleMediaManager1.setPreferredPayloadType(jingleMediaManager1.getPayloads().get(2));
List<JingleMediaManager> jml1 = new ArrayList<JingleMediaManager>();
jml1.add(jingleMediaManager1);
final JingleManager jm0 = new JingleManager(x0, jml0);
final JingleManager jm1 = new JingleManager(x1, jml1);
jm0.addCreationListener(icetm0);
jm1.addCreationListener(icetm1);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
JingleSession session = request.accept();
try {
Thread.sleep(12000);
} catch (InterruptedException e) {
e.printStackTrace();
}
session.startIncoming();
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
for (int i = 0; i < 10; i++) {
JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser());
// js0.addStateListener(new JingleSessionStateListener() {
//
// public void beforeChange(JingleNegotiator.State old, JingleNegotiator.State newOne)
// throws JingleNegotiator.JingleException {
// }
//
// public void afterChanged(JingleNegotiator.State old, JingleNegotiator.State newOne) {
// if (newOne != null) {
// if ((newOne instanceof OutgoingJingleSession.Active))
// System.err.println("|||" + newOne.getClass().getCanonicalName() + "|||");
// }
// }
// });
js0.startOutgoing();
Thread.sleep(45000);
js0.terminate();
Thread.sleep(1500);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void testCompleteSpeex() {
try {
//TCPConnection.DEBUG_ENABLED = true;
XMPPTCPConnection x0 = getConnection(0);
XMPPTCPConnection x1 = getConnection(1);
JingleMediaManager jingleMediaManager0 = new SpeexMediaManager(new STUNTransportManager());
JingleMediaManager jingleMediaManager1 = new SpeexMediaManager(new STUNTransportManager());
List<JingleMediaManager> jml0 = new ArrayList<JingleMediaManager>();
List<JingleMediaManager> jml1 = new ArrayList<JingleMediaManager>();
jml0.add(jingleMediaManager0);
jml1.add(jingleMediaManager1);
final JingleManager jm0 = new JingleManager(x0, jml0);
final JingleManager jm1 = new JingleManager(x1, jml1);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
JingleSession session = request.accept();
session.startIncoming();
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser());
js0.startOutgoing();
Thread.sleep(150000);
js0.terminate();
Thread.sleep(6000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void testCompleteScreenShare() {
try {
XMPPTCPConnection x0 = getConnection(0);
XMPPTCPConnection x1 = getConnection(1);
ICETransportManager icetm0 = new ICETransportManager(x0, "stun.xten.net", 3478);
ICETransportManager icetm1 = new ICETransportManager(x1, "stun.xten.net", 3478);
JingleMediaManager jingleMediaManager0 = new ScreenShareMediaManager(icetm0);
JingleMediaManager jingleMediaManager1 = new ScreenShareMediaManager(icetm1);
List<JingleMediaManager> jml0 = new ArrayList<JingleMediaManager>();
List<JingleMediaManager> jml1 = new ArrayList<JingleMediaManager>();
jml0.add(jingleMediaManager0);
jml1.add(jingleMediaManager1);
final JingleManager jm0 = new JingleManager(x0, jml0);
final JingleManager jm1 = new JingleManager(x1, jml1);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
JingleSession session = request.accept();
session.startIncoming();
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser());
js0.startOutgoing();
Thread.sleep(150000);
js0.terminate();
Thread.sleep(6000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void testCompleteWithBridge() {
for (int i = 0; i < 1; i += 2) {
final int n = i;
Thread t = new Thread(new Runnable() {
public void run() {
try {
XMPPTCPConnection x0 = getConnection(n);
XMPPTCPConnection x1 = getConnection(n + 1);
BridgedTransportManager btm0 = new BridgedTransportManager(x0);
BridgedTransportManager btm1 = new BridgedTransportManager(x1);
JingleMediaManager jingleMediaManager0 = new JmfMediaManager(btm0);
JingleMediaManager jingleMediaManager1 = new JmfMediaManager(btm1);
List<JingleMediaManager> jml0 = new ArrayList<JingleMediaManager>();
List<JingleMediaManager> jml1 = new ArrayList<JingleMediaManager>();
jml0.add(jingleMediaManager0);
jml1.add(jingleMediaManager1);
final JingleManager jm0 = new JingleManager(x0, jml0);
final JingleManager jm1 = new JingleManager(x1, jml1);
jm0.addCreationListener(btm0);
jm1.addCreationListener(btm1);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
JingleSession session = request.accept();
session.startIncoming();
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser());
js0.startOutgoing();
Thread.sleep(20000);
//js0.sendFormattedError(JingleError.UNSUPPORTED_TRANSPORTS);
js0.sendPacket(js0.createJingleError(null, JingleError.UNSUPPORTED_TRANSPORTS));
Thread.sleep(20000);
js0.terminate();
Thread.sleep(3000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.start();
}
try {
Thread.sleep(250000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void testCompleteWithBridgeB() {
try {
//TCPConnection.DEBUG_ENABLED = true;
XMPPTCPConnection x0 = getConnection(0);
XMPPTCPConnection x1 = getConnection(1);
BridgedTransportManager btm0 = new BridgedTransportManager(x0);
BridgedTransportManager btm1 = new BridgedTransportManager(x1);
JingleMediaManager jingleMediaManager0 = new JmfMediaManager(btm0);
JingleMediaManager jingleMediaManager1 = new JmfMediaManager(btm1);
List<JingleMediaManager> jml0 = new ArrayList<JingleMediaManager>();
List<JingleMediaManager> jml1 = new ArrayList<JingleMediaManager>();
jml0.add(jingleMediaManager0);
jml1.add(jingleMediaManager1);
final JingleManager jm0 = new JingleManager(x0, jml0);
final JingleManager jm1 = new JingleManager(x1, jml1);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
JingleSession session = request.accept();
session.startIncoming();
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser());
js0.startOutgoing();
Thread.sleep(20000);
js0.terminate();
Thread.sleep(3000);
js0 = jm0.createOutgoingJingleSession(x1.getUser());
js0.startOutgoing();
Thread.sleep(20000);
js0.terminate();
Thread.sleep(3000);
x0.disconnect();
x1.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void testAudioChannelOpenClose() {
for (int i = 0; i < 5; i++) {
try {
AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost()
.getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020, new AudioFormat(
AudioFormat.GSM_RTP), null);
AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost()
.getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002, new AudioFormat(
AudioFormat.GSM_RTP), null);
audioChannel0.start();
audioChannel1.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.stop();
audioChannel1.stop();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void testAudioChannelStartStop() {
try {
AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost()
.getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020,
new AudioFormat(AudioFormat.GSM_RTP), null);
AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost()
.getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002,
new AudioFormat(AudioFormat.GSM_RTP), null);
for (int i = 0; i < 5; i++) {
audioChannel0.start();
audioChannel1.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.stop();
audioChannel1.stop();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected int getMaxConnections() {
return 2;
}
}

View file

@ -0,0 +1,84 @@
/**
*
* 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;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.mediaimpl.test.TestMediaManager;
import org.jivesoftware.smackx.jingle.nat.FixedResolver;
import org.jivesoftware.smackx.jingle.nat.FixedTransportManager;
import java.util.ArrayList;
import java.util.List;
public class JingleSessionTest extends SmackTestCase {
public JingleSessionTest(final String name) {
super(name);
}
public void testEqualsObject() {
FixedResolver tr1 = new FixedResolver("127.0.0.1", 54222);
FixedTransportManager ftm1 = new FixedTransportManager(tr1);
TestMediaManager tmm1 = new TestMediaManager(ftm1);
List<JingleMediaManager> trl1 = new ArrayList<JingleMediaManager>();
trl1.add(tmm1);
JingleSession js1 = new JingleSession(getConnection(0), "res1", null, "10", trl1);
JingleSession js2 = new JingleSession(getConnection(1), "res1", null, "10", trl1);
JingleSession js3 = new JingleSession(getConnection(2), "res2", null, "11", trl1);
System.out.println(js1.getSid());
System.out.println(js2.getSid());
js1.setInitiator("js1");
js2.setInitiator("js1");
js1.setSid("10");
js2.setSid("10");
assertEquals(js1, js2);
assertEquals(js2, js1);
assertFalse(js1.equals(js3));
}
public void testGetInstanceFor() {
String ini1 = "initiator1";
String sid1 = "sid1";
String ini2 = "initiator2";
String sid2 = "sid2";
FixedResolver tr1 = new FixedResolver("127.0.0.1", 54222);
FixedTransportManager ftm1 = new FixedTransportManager(tr1);
TestMediaManager tmm1 = new TestMediaManager(ftm1);
List<JingleMediaManager> trl1 = new ArrayList<JingleMediaManager>();
trl1.add(tmm1);
JingleSession js1 = new JingleSession(getConnection(0), ini1, null, sid1, trl1);
JingleSession js2 = new JingleSession(getConnection(1), ini2, null, sid2, trl1);
// For a packet, we should be able to get a session that handles that...
assertNotNull(JingleSession.getInstanceFor(getConnection(0)));
assertNotNull(JingleSession.getInstanceFor(getConnection(1)));
assertEquals(JingleSession.getInstanceFor(getConnection(0)), js1);
assertEquals(JingleSession.getInstanceFor(getConnection(1)), js2);
}
protected int getMaxConnections() {
return 3;
}
}

View file

@ -0,0 +1,37 @@
/**
*
* 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;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Test suite that runs all the Jingle support tests
*
* @author Alvaro Saurin
*/
public class JingleSupportTests {
public static Test suite() {
TestSuite suite = new TestSuite("High and low level API tests for Jingle support");
// $JUnit-BEGIN$
suite.addTest(new TestSuite(JingleManagerTest.class));
// $JUnit-END$
return suite;
}
}

View file

@ -0,0 +1,104 @@
/**
*
* 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;
import java.util.ArrayList;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.media.PayloadType.Audio;
public class PayloadTypeTest extends SmackTestCase {
public PayloadTypeTest(final String arg0) {
super(arg0);
}
public void testEqualsObject() {
PayloadType p1 = new PayloadType(0, "pt1", 2);
PayloadType p2 = new PayloadType(0, "pt1", 2);
assertTrue(p1.equals(p2));
}
/**
* Test for the difference of payloads.
*/
public void testDifference() {
ArrayList<Audio> set1 = new ArrayList<Audio>();
ArrayList<Audio> set2 = new ArrayList<Audio>();
PayloadType.Audio common1 = new PayloadType.Audio(34, "supercodec-1", 2, 14000);
PayloadType.Audio common2 = new PayloadType.Audio(56, "supercodec-2", 1, 44000);
set1.add(common1);
set1.add(common2);
set1.add(new PayloadType.Audio(36, "supercodec-3", 2, 28000));
set1.add(new PayloadType.Audio(45, "supercodec-4", 1, 98000));
set2.add(new PayloadType.Audio(27, "supercodec-3", 2, 28000));
set2.add(common2);
set2.add(new PayloadType.Audio(32, "supercodec-4", 1, 98000));
set2.add(common1);
// Get the difference
ArrayList<Audio> commonSet = new ArrayList<Audio>();
commonSet.addAll(set1);
commonSet.retainAll(set2);
assertTrue(commonSet.size() == 2);
System.out.println("Codec " + ((PayloadType.Audio)commonSet.get(0)).getId());
System.out.println("Codec " + ((PayloadType.Audio)commonSet.get(1)).getId());
assertTrue(commonSet.contains(common1));
assertTrue(commonSet.contains(common2));
}
/**
* Test for the difference of payloads when we are handling the same sets.
*/
public void testDifferenceSameSet() {
ArrayList<Audio> set1 = new ArrayList<Audio>();
ArrayList<Audio> set2 = new ArrayList<Audio>();
PayloadType.Audio common1 = new PayloadType.Audio(34, "supercodec-1", 2, 14000);
PayloadType.Audio common2 = new PayloadType.Audio(56, "supercodec-2", 1, 44000);
PayloadType.Audio common3 = new PayloadType.Audio(0, "supercodec-3", 1, 44000);
PayloadType.Audio common4 = new PayloadType.Audio(120, "supercodec-4", 2, 66060);
set1.add(common1);
set1.add(common2);
set1.add(common3);
set1.add(common4);
set2.add(common1);
set2.add(common2);
set2.add(common3);
set2.add(common4);
// Get the difference
ArrayList<Audio> commonSet = new ArrayList<Audio>();
commonSet.addAll(set1);
commonSet.retainAll(set2);
assertTrue(commonSet.size() == 4);
assertTrue(commonSet.contains(common1));
assertTrue(commonSet.contains(common2));
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,117 @@
/**
*
* 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.nat;
import org.jivesoftware.smack.test.SmackTestCase;
import java.util.ArrayList;
public class BasicResolverTest extends SmackTestCase {
private int counter;
private final Object mutex = new Object();
public BasicResolverTest(String arg) {
super(arg);
}
// Counter management
private void resetCounter() {
synchronized (mutex) {
counter = 0;
}
}
private void incCounter() {
synchronized (mutex) {
counter++;
}
}
private int valCounter() {
int val;
synchronized (mutex) {
val = counter;
}
return val;
}
public void testCheckValidHostname() {
String validHostname = new String("slashdot.org");
BasicResolver br = new BasicResolver();
TransportCandidate tc = new TransportCandidate.Fixed(validHostname, 0);
resetCounter();
tc.addListener(new TransportResolverListener.Checker() {
public void candidateChecked(TransportCandidate cand, boolean result) {
if(result == true) {
System.out.println(cand.getIp() + " is reachable (as expected)");
incCounter();
}
}
public void candidateChecking(TransportCandidate cand) {
}
});
tc.check(new ArrayList<TransportCandidate>());
try {
Thread.sleep(TransportResolver.CHECK_TIMEOUT);
} catch (Exception e) {
}
assertTrue(valCounter() > 0);
}
public void testCheckInvalidHostname() {
String invalidHostname = new String("camupilosupino.org");
BasicResolver br = new BasicResolver();
TransportCandidate tc = new TransportCandidate.Fixed(invalidHostname, 0);
resetCounter();
tc.addListener(new TransportResolverListener.Checker() {
public void candidateChecked(TransportCandidate cand, boolean result) {
if(result == false) {
System.out.println(cand.getIp() + " is _not_ reachable (as expected)");
incCounter();
}
}
public void candidateChecking(TransportCandidate cand) {
}
});
tc.check(new ArrayList<TransportCandidate>());
try {
Thread.sleep(TransportResolver.CHECK_TIMEOUT);
} catch (Exception e) {
}
assertTrue(valCounter() > 0);
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,119 @@
/**
*
* 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.nat;
import org.jivesoftware.smack.test.SmackTestCase;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class BridgedResolverTest extends SmackTestCase {
private int counter;
private final Object mutex = new Object();
public BridgedResolverTest(String arg) {
super(arg);
}
// Counter management
private void resetCounter() {
synchronized (mutex) {
counter = 0;
}
}
private void incCounter() {
synchronized (mutex) {
counter++;
}
}
private int valCounter() {
int val;
synchronized (mutex) {
val = counter;
}
return val;
}
public void testCheckService() {
assertTrue(RTPBridge.serviceAvailable(getConnection(0)));
}
public void testGetBridge() {
resetCounter();
RTPBridge rtpBridge = RTPBridge.getRTPBridge(getConnection(0), "001");
System.out.println(rtpBridge.getIp() + " portA:" + rtpBridge.getPortA() + " portB:" + rtpBridge.getPortB());
if (rtpBridge != null) {
if (rtpBridge.getIp() != null) incCounter();
if (rtpBridge.getPortA() != -1) incCounter();
if (rtpBridge.getPortB() != -1) incCounter();
}
assertTrue(valCounter() == 3);
}
public void testGetPublicIp() {
resetCounter();
String publicIp = RTPBridge.getPublicIP(getConnection(0));
System.out.println(publicIp);
if (publicIp != null) {
incCounter();
}
try {
InetAddress localaddr = InetAddress.getLocalHost();
System.out.println("main Local IP Address : " + localaddr.getHostAddress());
System.out.println("main Local hostname : " + localaddr.getHostName());
InetAddress[] localaddrs = InetAddress.getAllByName("localhost");
for (int i = 0; i < localaddrs.length; i++) {
if (!localaddrs[i].equals(localaddr)) {
System.out.println("alt Local IP Address : " + localaddrs[i].getHostAddress());
System.out.println("alt Local hostname : " + localaddrs[i].getHostName());
System.out.println();
}
}
}
catch (UnknownHostException e) {
System.err.println("Can't detect localhost : " + e);
}
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
assertTrue(valCounter() == 1);
}
protected int getMaxConnections() {
return 1;
}
}

View file

@ -0,0 +1,25 @@
/**
*
* 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.nat;
import junit.framework.TestCase;
public class LocalhostTest extends TestCase {
public void testGetLocalhost() {
System.out.println(BridgedResolver.getLocalHost());
}
}

View file

@ -0,0 +1,406 @@
/**
*
* 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.nat;
import de.javawi.jstun.test.demo.StunServer;
import de.javawi.jstun.test.demo.ice.Candidate;
import de.javawi.jstun.test.demo.ice.ICENegociator;
import de.javawi.jstun.util.UtilityException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.mediaimpl.test.TestMediaManager;
import org.jivesoftware.smackx.jingle.nat.STUNResolver.STUNService;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/**
* Test the STUN IP resolver.
*
* @author Thiago Camargo
*/
public class STUNResolverTest extends SmackTestCase {
// Counter management
public STUNResolverTest(final String arg) {
super(arg);
}
private int counter;
private final Object mutex = new Object();
private void resetCounter() {
synchronized (mutex) {
counter = 0;
}
}
private void incCounter() {
synchronized (mutex) {
counter++;
}
}
private int valCounter() {
int val;
synchronized (mutex) {
val = counter;
}
return val;
}
/**
* Test for getPreferredCandidate()
*
* @throws Exception
*/
public void testGetPreferredCandidate() throws Exception {
int highestPref = 100;
TransportCandidate cand1 = new ICECandidate("192.168.2.1", 3, 2, "password", 3468, "username1", 1, ICECandidate.Type.prflx);
TransportCandidate cand2 = new ICECandidate("192.168.5.1", 2, 10, "password", 3469, "username2", 15,
ICECandidate.Type.prflx);
TransportCandidate candH = new ICECandidate("192.168.2.11", 1, 2, "password", 3468, "usernameH", highestPref,
ICECandidate.Type.prflx);
TransportCandidate cand3 = new ICECandidate("192.168.2.10", 2, 10, "password", 3469, "username3", 2,
ICECandidate.Type.prflx);
TransportCandidate cand4 = new ICECandidate("192.168.4.1", 3, 2, "password", 3468, "username4", 78, ICECandidate.Type.prflx);
STUNResolver stunResolver = new STUNResolver() {
};
stunResolver.addCandidate(cand1);
stunResolver.addCandidate(cand2);
stunResolver.addCandidate(candH);
stunResolver.addCandidate(cand3);
stunResolver.addCandidate(cand4);
assertEquals(stunResolver.getPreferredCandidate(), candH);
}
/**
* Test for getPreferredCandidate()
*
* @throws Exception
*/
public void testGetPreferredCandidateICE() throws Exception {
int highestPref = 100;
TransportCandidate cand1 = new ICECandidate("192.168.2.1", 3, 2, "password", 3468, "username1", 1, ICECandidate.Type.prflx);
TransportCandidate cand2 = new ICECandidate("192.168.5.1", 2, 10, "password", 3469, "username2", 15,
ICECandidate.Type.prflx);
TransportCandidate candH = new ICECandidate("192.168.2.11", 1, 2, "password", 3468, "usernameH", highestPref,
ICECandidate.Type.prflx);
TransportCandidate cand3 = new ICECandidate("192.168.2.10", 2, 10, "password", 3469, "username3", 2,
ICECandidate.Type.prflx);
TransportCandidate cand4 = new ICECandidate("192.168.4.1", 3, 2, "password", 3468, "username4", 78, ICECandidate.Type.prflx);
ICEResolver iceResolver = new ICEResolver(getConnection(0), "stun.xten.net", 3478) {
};
iceResolver.addCandidate(cand1);
iceResolver.addCandidate(cand2);
iceResolver.addCandidate(candH);
iceResolver.addCandidate(cand3);
iceResolver.addCandidate(cand4);
assertEquals(iceResolver.getPreferredCandidate(), candH);
}
/**
* Test priority generated by STUN lib
*
* @throws Exception
*/
public void testICEPriority() throws Exception {
String first = "";
for (int i = 0; i < 100; i++) {
ICENegociator cc = new ICENegociator((short) 1);
// gather candidates
cc.gatherCandidateAddresses();
// priorize candidates
cc.prioritizeCandidates();
// get SortedCandidates
for (Candidate candidate : cc.getSortedCandidates()) {
short nicNum = 0;
try {
Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
short tempNic = 0;
NetworkInterface nic = NetworkInterface.getByInetAddress(candidate.getAddress().getInetAddress());
while(nics.hasMoreElements()) {
NetworkInterface checkNIC = nics.nextElement();
if (checkNIC.equals(nic)) {
nicNum = tempNic;
break;
}
i++;
}
} catch (SocketException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
TransportCandidate transportCandidate = new ICECandidate(candidate.getAddress().getInetAddress()
.getHostAddress(), 1, nicNum, "1", candidate.getPort(), "1", candidate.getPriority(),
ICECandidate.Type.prflx);
transportCandidate.setLocalIp(candidate.getBase().getAddress().getInetAddress().getHostAddress());
System.out.println("C: " + candidate.getAddress().getInetAddress() + "|"
+ candidate.getBase().getAddress().getInetAddress() + " p:" + candidate.getPriority());
} catch (UtilityException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
Candidate candidate = cc.getSortedCandidates().get(0);
String temp = "C: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress()
+ " p:" + candidate.getPriority();
if (first.equals(""))
first = temp;
assertEquals(first, temp);
first = temp;
}
}
/**
* Test for loadSTUNServers()
*
* @throws Exception
*/
public void testLoadSTUNServers() throws Exception {
STUNResolver stunResolver = new STUNResolver() {
};
ArrayList<STUNService> stunServers = stunResolver.loadSTUNServers();
assertTrue(stunServers.size() > 0);
System.out.println(stunServers.size() + " servers loaded");
}
public void testGetSTUNServer() {
System.out.println(STUN.serviceAvailable(getConnection(0)));
STUN stun = STUN.getSTUNServer(getConnection(0));
for (STUN.StunServerAddress stunServerAddress : stun.getServers())
System.out.println(stunServerAddress.getServer() + ":" + stunServerAddress.getPort());
System.out.println(stun.getPublicIp());
}
/**
* Test for resolve()
*
* @throws Exception
*/
public void testResolve() throws Exception {
final STUNResolver stunResolver = new STUNResolver() {
};
stunResolver.addListener(new TransportResolverListener.Resolver() {
public void candidateAdded(final TransportCandidate cand) {
incCounter();
String addr = cand.getIp();
int port = cand.getPort();
System.out.println("Addr: " + addr + " port:" + port);
}
public void init() {
System.out.println("Resolution started");
}
public void end() {
System.out.println("Resolution finished");
}
});
try {
stunResolver.initializeAndWait();
Thread.sleep(55000);
assertTrue(valCounter() > 0);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Generate a list of payload types
*
* @return A testing list
*/
private ArrayList<PayloadType> getTestPayloads1() {
ArrayList<PayloadType> result = new ArrayList<PayloadType>();
result.add(new PayloadType.Audio(34, "supercodec-1", 2, 14000));
result.add(new PayloadType.Audio(56, "supercodec-2", 1, 44000));
result.add(new PayloadType.Audio(36, "supercodec-3", 2, 28000));
result.add(new PayloadType.Audio(45, "supercodec-4", 1, 98000));
return result;
}
private ArrayList<PayloadType> getTestPayloads2() {
ArrayList<PayloadType> result = new ArrayList<PayloadType>();
result.add(new PayloadType.Audio(27, "supercodec-3", 2, 28000));
result.add(new PayloadType.Audio(56, "supercodec-2", 1, 44000));
result.add(new PayloadType.Audio(32, "supercodec-4", 1, 98000));
result.add(new PayloadType.Audio(34, "supercodec-1", 2, 14000));
return result;
}
/**
* This is a simple test where the user_2 rejects the Jingle session.
*/
public void testSTUNJingleSession() {
resetCounter();
try {
TransportResolver tr1 = new STUNResolver() {
};
TransportResolver tr2 = new STUNResolver() {
};
// Explicit resolution
tr1.resolve(null);
tr2.resolve(null);
STUNTransportManager stm0 = new STUNTransportManager();
TestMediaManager tmm0 = new TestMediaManager(stm0);
tmm0.setPayloads(getTestPayloads1());
List<JingleMediaManager> trl0 = new ArrayList<JingleMediaManager>();
trl0.add(tmm0);
STUNTransportManager stm1 = new STUNTransportManager();
TestMediaManager tmm1 = new TestMediaManager(stm1);
tmm1.setPayloads(getTestPayloads2());
List<JingleMediaManager> trl1 = new ArrayList<JingleMediaManager>();
trl1.add(tmm1);
final JingleManager man0 = new JingleManager(getConnection(0), trl0);
final JingleManager man1 = new JingleManager(getConnection(1), trl1);
man1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
/**
* Called when a new session request is detected
*/
public void sessionRequested(final JingleSessionRequest request) {
System.out.println("Session request detected, from " + request.getFrom() + ": accepting.");
// We accept the request
JingleSession session1;
try {
session1 = request.accept();
session1.addListener(new JingleSessionListener() {
public void sessionClosed(String reason, JingleSession jingleSession) {
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
}
public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc,
JingleSession jingleSession) {
incCounter();
System.out.println("Responder: the session is fully established.");
System.out.println("+ Payload Type: " + pt.getId());
System.out.println("+ Local IP/port: " + lc.getIp() + ":" + lc.getPort());
System.out.println("+ Remote IP/port: " + rc.getIp() + ":" + rc.getPort());
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
}
public void sessionMediaReceived(JingleSession jingleSession, String participant) {
// Do Nothing
}
});
session1.startIncoming();
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
// Session 0 starts a request
System.out.println("Starting session request, to " + getFullJID(1) + "...");
JingleSession session0 = man0.createOutgoingJingleSession(getFullJID(1));
session0.addListener(new JingleSessionListener() {
public void sessionClosed(String reason, JingleSession jingleSession) {
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
}
public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc,
JingleSession jingleSession) {
incCounter();
System.out.println("Initiator: the session is fully established.");
System.out.println("+ Payload Type: " + pt.getId());
System.out.println("+ Local IP/port: " + lc.getIp() + ":" + lc.getPort());
System.out.println("+ Remote IP/port: " + rc.getIp() + ":" + rc.getPort());
}
public void sessionMediaReceived(JingleSession jingleSession, String participant) {
// Do Nothing
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
}
});
session0.startOutgoing();
Thread.sleep(60000);
assertTrue(valCounter() == 2);
} catch (Exception e) {
e.printStackTrace();
fail("An error occured with Jingle");
}
}
protected int getMaxConnections() {
return 2;
}
}

View file

@ -0,0 +1,74 @@
/**
*
* 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.nat;
import java.util.ArrayList;
import java.util.Collections;
import org.jivesoftware.smack.test.SmackTestCase;
public class TransportCandidateTest extends SmackTestCase {
public TransportCandidateTest(final String arg0) {
super(arg0);
}
/**
* Test for equals()
*/
public void testEqualsObject() {
TransportCandidate cand1 = new ICECandidate("192.168.2.1", 1, 2,
"password", 3468, "username", 25, ICECandidate.Type.prflx);
TransportCandidate cand2 = new ICECandidate("192.168.2.1", 1, 2,
"password", 3468, "username", 25, ICECandidate.Type.prflx);
TransportCandidate cand3 = new ICECandidate("192.168.2.1", 1, 2,
"password", 3469, "username", 25, ICECandidate.Type.prflx);
assertEquals(cand1, cand2);
assertFalse(cand1.equals(cand3));
}
/**
* Test for compareTo()
*/
public void testCompareTo() {
int highestPref = 100;
ICECandidate cand1 = new ICECandidate("192.168.2.1", 3, 2,
"password", 3468, "username", 1, ICECandidate.Type.prflx);
ICECandidate cand2 = new ICECandidate("192.168.5.1", 2, 10,
"password", 3469, "username", 15,ICECandidate.Type.prflx);
ICECandidate candH = new ICECandidate("192.168.2.1", 1, 2,
"password", 3468, "username", highestPref, ICECandidate.Type.prflx);
ICECandidate cand3 = new ICECandidate("192.168.2.10", 2, 10,
"password", 3469, "username", 2, ICECandidate.Type.prflx);
ICECandidate cand4 = new ICECandidate("192.168.4.1", 3, 2,
"password", 3468, "username", 78, ICECandidate.Type.prflx);
ArrayList<ICECandidate> candList = new ArrayList<ICECandidate>();
candList.add(cand1);
candList.add(cand2);
candList.add(candH);
candList.add(cand3);
candList.add(cand4);
Collections.sort(candList);
assertEquals(candList.get(candList.size() - 1), candH);
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,64 @@
/**
*
* 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.nat;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
public class TransportResolverTest extends SmackTestCase {
public TransportResolverTest(final String arg) {
super(arg);
}
public void testIsResolving() {
final TransportResolver tr = new BasicResolver();
tr.addListener(
new TransportResolverListener.Resolver() {
public void candidateAdded(final TransportCandidate cand) {
System.out.println("candidateAdded() called.");
assertTrue(tr.isResolving() || (!tr.isResolving() && tr.isResolved()));
}
public void end() {
System.out.println("end() called.");
assertFalse(tr.isResolving());
assertTrue(tr.isResolved());
}
public void init() {
System.out.println("init() called.");
assertTrue(tr.isResolving());
assertFalse(tr.isResolved());
}
});
assertFalse(tr.isResolving());
assertFalse(tr.isResolved());
try {
tr.resolve(null);
} catch (XMPPException e) {
e.printStackTrace();
fail("Error resolving");
}
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,126 @@
/**
*
* 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.provider;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.packet.Jingle;
public class JingleProviderTest extends SmackTestCase {
public JingleProviderTest(final String name) {
super(name);
}
public void testProviderManager() {
IQProvider iqProv;
String elementNamee = Jingle.getElementName();
String nameSpace = Jingle.getNamespace();
System.out.println("Testing if the Jingle IQ provider is registered...");
// Verify that the Jingle IQProvider is registered.
iqProv = (IQProvider)ProviderManager.getInstance().getIQProvider(elementNamee, nameSpace);
assertNotNull(iqProv);
}
/**
* Test for parsing a Jingle
*/
public void testParseIQSimple() {
// Create a dummy packet for testing...
IQfake iqSent = new IQfake (
" <jingle xmlns='urn:xmpp:tmp:jingle'" +
" initiator=\"gorrino@viejo.com\"" +
" responder=\"colico@hepatico.com\"" +
" action=\"transport-info\" sid=\"\">" +
" <transport xmlns='urn:xmpp:tmp:jingle:transports:ice-udp'>" +
" <candidate generation=\"1\"" +
" ip=\"192.168.1.1\"" +
" password=\"secret\"" +
" port=\"8080\"" +
" username=\"username\"" +
" preference=\"1\"/>" +
" </transport>" +
"</jingle>");
iqSent.setTo(getFullJID(0));
iqSent.setFrom(getFullJID(0));
iqSent.setType(IQ.Type.get);
// Create a filter and a collector...
PacketFilter filter = new PacketTypeFilter(IQ.class);
PacketCollector collector = getConnection(0).createPacketCollector(filter);
System.out.println("Testing if a Jingle IQ can be sent and received...");
// Send the iq packet with an invalid namespace
getConnection(0).sendPacket(iqSent);
// Receive the packet
IQ iqReceived = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
// Stop queuing results
collector.cancel();
if (iqReceived == null) {
fail("No response from server");
}
else if (iqReceived.getType() == IQ.Type.error) {
fail("The server did reply with an error packet: " + iqReceived.getError().getCode());
}
else {
assertTrue(iqReceived instanceof Jingle);
Jingle jin = (Jingle) iqReceived;
System.out.println("Sent: " + iqSent.toXML());
System.out.println("Received: " + jin.toXML());
}
}
/**
* Simple class for testing an IQ...
* @author Alvaro Saurin
*/
private class IQfake extends IQ {
private String s;
public IQfake(final String s) {
super();
this.s = s;
}
public String getChildElementXML() {
StringBuilder buf = new StringBuilder();
buf.append(s);
return buf.toString();
}
}
protected int getMaxConnections() {
return 2;
}
}

View file

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- Default configuration for Smack test cases. -->
<testcase>
<!-- Host and port of the XMPP server to use -->
<host>localhost</host>
<port>5222</port>
<!-- Chat and MUC domain names to use -->
<chat>chat</chat>
<muc>conference</muc>
</testcase>

View file

@ -0,0 +1,341 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.listeners.JingleListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.MediaNegotiator;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
import org.jivesoftware.smackx.jingleold.nat.TransportNegotiator;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
import org.jivesoftware.smackx.jingleold.packet.JingleContent;
/**
* @author Jeff Williams
*/
public class ContentNegotiator extends JingleNegotiator {
public static final String INITIATOR = "initiator";
public static final String RESPONDER = "responder";
private List<TransportNegotiator> transportNegotiators;
private MediaNegotiator mediaNeg; // The description...
private TransportNegotiator transNeg; // and transport negotiators
private JingleTransportManager jingleTransportManager;
private String creator;
private String name;
private JingleMediaSession jingleMediaSession = null;
public ContentNegotiator(JingleSession session, String inCreator, String inName) {
super(session);
creator = inCreator;
name = inName;
transportNegotiators = new ArrayList<TransportNegotiator>();
}
public List<IQ> dispatchIncomingPacket(IQ iq, String id) throws XMPPException, SmackException {
List<IQ> responses = new ArrayList<IQ>();
// First only process IQ packets that contain <content> stanzas that
// match this media manager.
if (iq != null) {
if (iq.getType().equals(IQ.Type.error)) {
// Process errors
// TODO getState().eventError(iq);
} else if (iq.getType().equals(IQ.Type.result)) {
// Process ACKs
if (isExpectedId(iq.getPacketID())) {
removeExpectedId(iq.getPacketID());
}
} else if (iq instanceof Jingle) {
Jingle jingle = (Jingle) iq;
// There are 1 or more <content> sections in a Jingle packet.
// Find out which <content> section belongs to this content negotiator, and
// then dispatch the Jingle packet to the media and transport negotiators.
for (JingleContent jingleContent : jingle.getContentsList()) {
if (jingleContent.getName().equals(name)) {
if (mediaNeg != null) {
responses.addAll(mediaNeg.dispatchIncomingPacket(iq, id));
}
if (transNeg != null) {
responses.addAll(transNeg.dispatchIncomingPacket(iq, id));
}
}
}
}
}
return responses;
}
public String getCreator() {
return creator;
}
public String getName() {
return name;
}
/**
* Get the JingleMediaSession of this Jingle Session
*
* @return the JingleMediaSession
*/
public JingleMediaSession getJingleMediaSession() {
return jingleMediaSession;
}
public void addTransportNegotiator(TransportNegotiator transportNegotiator) {
transportNegotiators.add(transportNegotiator);
}
/**
* @param jingleTransportManager
*/
public void setJingleTransportManager(JingleTransportManager jingleTransportManager) {
this.jingleTransportManager = jingleTransportManager;
}
/**
* @return the JingleTransportManager
*/
public JingleTransportManager getTransportManager() {
return jingleTransportManager;
}
/**
* Called from above when starting a new session.
*/
protected void doStart() {
// JingleContent result = new JingleContent(creator, name);
// result.setDescription(mediaNeg.start());
// result.addJingleTransport(transNeg.start());
//
// return result;
mediaNeg.start();
transNeg.start();
}
/**
* Prepare to close the media manager.
*/
public void close() {
destroyMediaNegotiator();
destroyTransportNegotiator();
}
/**
* Obtain the description negotiator for this session
*
* @return the description negotiator
*/
public MediaNegotiator getMediaNegotiator() {
return mediaNeg;
}
/**
* Set the jmf negotiator.
*
* @param mediaNeg
* the description negotiator to set
*/
protected void setMediaNegotiator(MediaNegotiator mediaNeg) {
destroyMediaNegotiator();
this.mediaNeg = mediaNeg;
}
/**
* Destroy the jmf negotiator.
*/
protected void destroyMediaNegotiator() {
if (mediaNeg != null) {
mediaNeg.close();
mediaNeg = null;
}
}
/**
* Obtain the transport negotiator for this session.
*
* @return the transport negotiator instance
*/
public TransportNegotiator getTransportNegotiator() {
return transNeg;
}
/**
* Set TransportNegociator
*
* @param transNeg
* the transNeg to set
*/
protected void setTransportNegotiator(TransportNegotiator transNeg) {
destroyTransportNegotiator();
this.transNeg = transNeg;
}
/**
* Destroy the transport negotiator.
*/
protected void destroyTransportNegotiator() {
if (transNeg != null) {
transNeg.close();
transNeg = null;
}
}
/**
* Return true if the transport and content negotiators have finished
*/
public boolean isFullyEstablished() {
boolean result = true;
MediaNegotiator mediaNeg = getMediaNegotiator();
if ((mediaNeg == null) || (!mediaNeg.isFullyEstablished())) {
result = false;
}
TransportNegotiator transNeg = getTransportNegotiator();
if ((transNeg == null) || (!transNeg.isFullyEstablished())) {
result = false;
}
return result;
}
public JingleContent getJingleContent() {
JingleContent result = new JingleContent(creator, name);
// PayloadType.Audio bestCommonAudioPt = getMediaNegotiator().getBestCommonAudioPt();
// TransportCandidate bestRemoteCandidate = getTransportNegotiator().getBestRemoteCandidate();
//
// // Ok, send a packet saying that we accept this session
// // with the audio payload type and the transport
// // candidate
// result.setDescription(new JingleDescription.Audio(new PayloadType(bestCommonAudioPt)));
// result.addJingleTransport(this.getTransportNegotiator().getJingleTransport(bestRemoteCandidate));
if (mediaNeg != null) {
result.setDescription(mediaNeg.getJingleDescription());
}
if (transNeg != null) {
result.addJingleTransport(transNeg.getJingleTransport());
}
return result;
}
public void triggerContentEstablished() throws NotConnectedException {
PayloadType bestCommonAudioPt = getMediaNegotiator().getBestCommonAudioPt();
TransportCandidate bestRemoteCandidate = getTransportNegotiator().getBestRemoteCandidate();
TransportCandidate acceptedLocalCandidate = getTransportNegotiator().getAcceptedLocalCandidate();
// Trigger the session established flag
triggerContentEstablished(bestCommonAudioPt, bestRemoteCandidate, acceptedLocalCandidate);
}
/**
* Trigger a session established event.
* @throws NotConnectedException
*/
private void triggerContentEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc) throws NotConnectedException {
// Let the session know that we've established a content/media segment.
JingleSession session = getSession();
if (session != null) {
List<JingleListener> listeners = session.getListenersList();
for (JingleListener li : listeners) {
if (li instanceof JingleSessionListener) {
JingleSessionListener sli = (JingleSessionListener) li;
sli.sessionEstablished(pt, rc, lc, session);
}
}
}
// Create a media session for each media manager in the session.
if (mediaNeg.getMediaManager() != null) {
rc.removeCandidateEcho();
lc.removeCandidateEcho();
jingleMediaSession = getMediaNegotiator().getMediaManager().createMediaSession(pt, rc, lc, session);
jingleMediaSession.addMediaReceivedListener(session);
if (jingleMediaSession != null) {
jingleMediaSession.startTrasmit();
jingleMediaSession.startReceive();
for (TransportCandidate candidate : getTransportNegotiator().getOfferedCandidates())
candidate.removeCandidateEcho();
}
JingleMediaManager mediaManager = getMediaNegotiator().getMediaManager();
getSession().addJingleMediaSession(mediaManager.getName(), jingleMediaSession);
}
}
/**
* Stop a Jingle media session.
*/
public void stopJingleMediaSession() {
if (jingleMediaSession != null) {
jingleMediaSession.stopTrasmit();
jingleMediaSession.stopReceive();
}
}
/**
* The negotiator state for the ContentNegotiators is a special case.
* It is a roll-up of the sub-negotiator states.
*/
public JingleNegotiatorState getNegotiatorState() {
JingleNegotiatorState result = JingleNegotiatorState.PENDING;
if ((mediaNeg != null) && (transNeg != null)) {
if ((mediaNeg.getNegotiatorState() == JingleNegotiatorState.SUCCEEDED)
|| (transNeg.getNegotiatorState() == JingleNegotiatorState.SUCCEEDED))
result = JingleNegotiatorState.SUCCEEDED;
if ((mediaNeg.getNegotiatorState() == JingleNegotiatorState.FAILED)
|| (transNeg.getNegotiatorState() == JingleNegotiatorState.FAILED))
result = JingleNegotiatorState.FAILED;
}
// Store the state (to make it easier to know when debugging.)
setNegotiatorState(result);
return result;
}
}

View file

@ -0,0 +1,65 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
/**
* The "action" in the jingle packet, as an enum.
*
* Changed to reflect XEP-166 rev: 20JUN07
*
* @author Jeff Williams
*/
public enum JingleActionEnum {
UNKNOWN("unknown"),
CONTENT_ACCEPT("content-accept"),
CONTENT_ADD("content-add"),
CONTENT_MODIFY("content-modify"),
CONTENT_REMOVE("content-remove"),
SESSION_ACCEPT("session-accept"),
SESSION_INFO("session-info"),
SESSION_INITIATE("session-initiate"),
SESSION_TERMINATE("session-terminate"),
TRANSPORT_INFO("transport-info");
private String actionCode;
private JingleActionEnum(String inActionCode) {
actionCode = inActionCode;
}
/**
* Returns the String value for an Action.
*/
public String toString() {
return actionCode;
}
/**
* Returns the Action enum for a String action value.
*/
public static JingleActionEnum getAction(String inActionCode) {
for (JingleActionEnum jingleAction : JingleActionEnum.values()) {
if (jingleAction.actionCode.equals(inActionCode)) {
return jingleAction;
}
}
return null;
}
}

View file

@ -0,0 +1,69 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.packet.JingleError;
/**
* A Jingle exception.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public class JingleException extends XMPPException {
private static final long serialVersionUID = -1521230401958103382L;
private final JingleError error;
/**
* Default constructor.
*/
public JingleException() {
super();
error = null;
}
/**
* Constructor with an error message.
*
* @param msg The message.
*/
public JingleException(String msg) {
super(msg);
error = null;
}
/**
* Constructor with an error response.
*
* @param error The error message.
*/
public JingleException(JingleError error) {
super();
this.error = error;
}
/**
* Return the error message.
*
* @return the error
*/
public JingleError getError() {
return error;
}
}

View file

@ -0,0 +1,593 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.jingleold.listeners.CreatedJingleSessionListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.BasicTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
import org.jivesoftware.smackx.jingleold.nat.TransportResolver;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
import org.jivesoftware.smackx.jingleold.provider.JingleProvider;
import org.jxmpp.util.XmppStringUtils;
/**
* Jingle is a session establishment protocol defined in (XEP-0166).
* It defines a framework for negotiating and managing out-of-band ( data that is send and receive through other connection than XMPP connection) data sessions over XMPP.
* With this protocol you can setup VOIP Calls, Video Streaming, File transfers and whatever out-of-band session based transmission.
* <p/>
* To create a Jingle Session you need a Transport method and a Payload type.
* <p/>
* A transport method is how it will trasmit and receive network packets. Transport MUST have one or more candidates.
* A transport candidate is an IP Address with a defined port, that other party must send data to.
* <p/>
* A supported payload type, is the data encoding format that the jmf will be transmitted.
* For instance an Audio Payload "GSM".
* <p/>
* A Jingle session negociates a payload type and a pair of transport candidates.
* Which means that when a Jingle Session is establhished you will have two defined transport candidates with addresses
* and a defined Payload type.
* In other words, you will have two IP address with their respective ports, and a Codec type defined.
* <p/>
* The JingleManager is a facade built upon Jabber Jingle (XEP-166) to allow the
* use of Jingle. This implementation allows the user to simply
* use this class for setting the Jingle parameters, create and receive Jingle Sessions.
* <p/>
* In order to use the Jingle, the user must provide a
* TransportManager that will handle the resolution of potential IP addresses taht can be used to transport the streaming (jmf).
* This TransportManager can be initialized with several default resolvers,
* including a fixed solver that can be used when the address and port are know
* in advance.
* This API have ready to use Transport Managers, for instance: BasicTransportManager, STUNTransportManager, BridgedTransportManager.
* <p/>
* You should also especify a JingleMediaManager if you want that JingleManager assume Media control
* Using a JingleMediaManager implementation is the easier way to implement a Jingle Application.
* <p/>
* Otherwise before creating an outgoing connection, the user must create jingle session
* listeners that will be called when different events happen. The most
* important event is <i>sessionEstablished()</i>, that will be called when all
* the negotiations are finished, providing the payload type for the
* transmission as well as the remote and local addresses and ports for the
* communication. See JingleSessionListener for a complete list of events that can be
* observed.
* <p/>
* This is an example of how to use the JingleManager:
* <i>This example implements a Jingle VOIP Call between two users.</i>
* <p/>
* <pre>
* <p/>
* To wait for an Incoming Jingle Session:
* <p/>
* try {
* <p/>
* // Connect to a XMPP Server
* XMPPConnection x1 = new XMPPTCPConnection("xmpp.com");
* x1.connect();
* x1.login("juliet", "juliet");
* <p/>
* // Create a JingleManager using a BasicResolver
* final JingleManager jm1 = new JingleManager(
* x1, new BasicTransportManager());
* <p/>
* // Create a JingleMediaManager. In this case using Jingle Audio Media API
* JingleMediaManager jingleMediaManager = new AudioMediaManager();
* <p/>
* // Set the JingleMediaManager
* jm1.setMediaManager(jingleMediaManager);
* <p/>
* // Listen for incoming calls
* jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
* public void sessionRequested(JingleSessionRequest request) {
* <p/>
* try {
* // Accept the call
* IncomingJingleSession session = request.accept();
* <p/>
* <p/>
* // Start the call
* session.start();
* } catch (XMPPException e) {
* e.printStackTrace();
* }
* <p/>
* }
* });
* <p/>
* Thread.sleep(15000);
* <p/>
* } catch (Exception e) {
* e.printStackTrace();
* }
* <p/>
* To create an Outgoing Jingle Session:
* <p/>
* try {
* <p/>
* // Connect to a XMPP Server
* XMPPConnection x0 = new XMPPTCPConnection("xmpp.com");
* x0.connect();
* x0.login("romeo", "romeo");
* <p/>
* // Create a JingleManager using a BasicResolver
* final JingleManager jm0 = new JingleManager(
* x0, new BasicTransportManager());
* <p/>
* // Create a JingleMediaManager. In this case using Jingle Audio Media API
* JingleMediaManager jingleMediaManager = new AudioMediaManager(); // Using Jingle Media API
* <p/>
* // Set the JingleMediaManager
* jm0.setMediaManager(jingleMediaManager);
* <p/>
* // Create a new Jingle Call with a full JID
* OutgoingJingleSession js0 = jm0.createOutgoingJingleSession("juliet@xmpp.com/Smack");
* <p/>
* // Start the call
* js0.start();
* <p/>
* Thread.sleep(10000);
* js0.terminate();
* <p/>
* Thread.sleep(3000);
* <p/>
* } catch (Exception e) {
* e.printStackTrace();
* }
* </pre>
*
* @author Thiago Camargo
* @author Alvaro Saurin
* @author Jeff Williams
* @see JingleListener
* @see TransportResolver
* @see JingleSession
* @see JingleSession
* @see JingleMediaManager
* @see BasicTransportManager , STUNTransportManager, BridgedTransportManager, TransportResolver, BridgedResolver, ICEResolver, STUNResolver and BasicResolver.
*/
public class JingleManager implements JingleSessionListener {
private static final Logger LOGGER = Logger.getLogger(JingleManager.class.getName());
// non-static
final List<JingleSession> jingleSessions = new ArrayList<JingleSession>();
// Listeners for manager events (ie, session requests...)
private List<JingleSessionRequestListener> jingleSessionRequestListeners;
// Listeners for created JingleSessions
private List<CreatedJingleSessionListener> creationListeners = new ArrayList<CreatedJingleSessionListener>();
// The XMPP connection
private XMPPConnection connection;
// The Media Managers
private List<JingleMediaManager> jingleMediaManagers;
/**
* Default constructor with a defined XMPPConnection, Transport Resolver and a Media Manager
* If a fully implemented JingleMediaSession is entered, JingleManager manage Jingle signalling and jmf
*
* @param connection XMPP XMPPConnection to be used
* @param jingleMediaManagers an implemeted JingleMediaManager to be used.
* @throws SmackException
* @throws XMPPException
*/
public JingleManager(XMPPConnection connection, List<JingleMediaManager> jingleMediaManagers) throws XMPPException, SmackException {
this.connection = connection;
this.jingleMediaManagers = jingleMediaManagers;
connection.getRoster().addRosterListener(new RosterListener() {
public void entriesAdded(Collection<String> addresses) {
}
public void entriesUpdated(Collection<String> addresses) {
}
public void entriesDeleted(Collection<String> addresses) {
}
public void presenceChanged(Presence presence) {
if (!presence.isAvailable()) {
String xmppAddress = presence.getFrom();
JingleSession aux = null;
for (JingleSession jingleSession : jingleSessions) {
if (jingleSession.getInitiator().equals(xmppAddress) || jingleSession.getResponder().equals(xmppAddress)) {
aux = jingleSession;
}
}
if (aux != null)
try {
aux.terminate();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
/**
* Setup the jingle system to let the remote clients know we support Jingle.
* (This used to be a static part of construction. The problem is a remote client might
* attempt a Jingle connection to us after we've created a XMPPConnection, but before we've
* setup an instance of a JingleManager. We will appear to not support Jingle. With the new
* method you just call it once and all new connections will report Jingle support.)
*/
public static void setJingleServiceEnabled() {
ProviderManager.addIQProvider("jingle", "urn:xmpp:tmp:jingle", new JingleProvider());
// Enable the Jingle support on every established connection
// The ServiceDiscoveryManager class should have been already
// initialized
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
public void connectionCreated(XMPPConnection connection) {
JingleManager.setServiceEnabled(connection, true);
}
});
}
/**
* Enables or disables the Jingle support on a given connection.
* <p/>
* <p/>
* Before starting any Jingle jmf session, check that the user can handle
* it. Enable the Jingle support to indicate that this client handles Jingle
* messages.
*
* @param connection the connection where the service will be enabled or
* disabled
* @param enabled indicates if the service will be enabled or disabled
*/
public synchronized static void setServiceEnabled(XMPPConnection connection, boolean enabled) {
if (isServiceEnabled(connection) == enabled) {
return;
}
if (enabled) {
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(Jingle.NAMESPACE);
} else {
ServiceDiscoveryManager.getInstanceFor(connection).removeFeature(Jingle.NAMESPACE);
}
}
/**
* Returns true if the Jingle support is enabled for the given connection.
*
* @param connection the connection to look for Jingle support
* @return a boolean indicating if the Jingle support is enabled for the
* given connection
*/
public static boolean isServiceEnabled(XMPPConnection connection) {
return ServiceDiscoveryManager.getInstanceFor(connection).includesFeature(Jingle.NAMESPACE);
}
/**
* Returns true if the specified user handles Jingle messages.
*
* @param connection the connection to use to perform the service discovery
* @param userID the user to check. A fully qualified xmpp ID, e.g.
* jdoe@example.com
* @return a boolean indicating whether the specified user handles Jingle
* messages
* @throws SmackException if there was no response from the server.
* @throws XMPPException
*/
public static boolean isServiceEnabled(XMPPConnection connection, String userID) throws XMPPException, SmackException {
return ServiceDiscoveryManager.getInstanceFor(connection).supportsFeature(userID, Jingle.NAMESPACE);
}
/**
* Get the Media Managers of this Jingle Manager
*
* @return the list of JingleMediaManagers
*/
public List<JingleMediaManager> getMediaManagers() {
return jingleMediaManagers;
}
/**
* Set the Media Managers of this Jingle Manager
*
* @param jingleMediaManagers JingleMediaManager to be used for open, close, start and stop jmf streamings
*/
public void setMediaManagers(List<JingleMediaManager> jingleMediaManagers) {
this.jingleMediaManagers = jingleMediaManagers;
}
/**
* Add a Jingle session request listenerJingle to listen to incoming session
* requests.
*
* @param jingleSessionRequestListener an implemented JingleSessionRequestListener
* @see #removeJingleSessionRequestListener(JingleSessionRequestListener)
* @see JingleListener
*/
public synchronized void addJingleSessionRequestListener(final JingleSessionRequestListener jingleSessionRequestListener) {
if (jingleSessionRequestListener != null) {
if (jingleSessionRequestListeners == null) {
initJingleSessionRequestListeners();
}
synchronized (jingleSessionRequestListeners) {
jingleSessionRequestListeners.add(jingleSessionRequestListener);
}
}
}
/**
* Removes a Jingle session listenerJingle.
*
* @param jingleSessionRequestListener The jingle session jingleSessionRequestListener to be removed
* @see #addJingleSessionRequestListener(JingleSessionRequestListener)
* @see JingleListener
*/
public void removeJingleSessionRequestListener(JingleSessionRequestListener jingleSessionRequestListener) {
if (jingleSessionRequestListeners == null) {
return;
}
synchronized (jingleSessionRequestListeners) {
jingleSessionRequestListeners.remove(jingleSessionRequestListener);
}
}
/**
* Adds a CreatedJingleSessionListener.
* This listener will be called when a session is created by the JingleManager instance.
*
* @param createdJingleSessionListener
*/
public void addCreationListener(CreatedJingleSessionListener createdJingleSessionListener) {
this.creationListeners.add(createdJingleSessionListener);
}
/**
* Removes a CreatedJingleSessionListener.
* This listener will be called when a session is created by the JingleManager instance.
*
* @param createdJingleSessionListener
*/
public void removeCreationListener(CreatedJingleSessionListener createdJingleSessionListener) {
this.creationListeners.remove(createdJingleSessionListener);
}
/**
* Trigger CreatedJingleSessionListeners that a session was created.
*
* @param jingleSession
*/
public void triggerSessionCreated(JingleSession jingleSession) {
jingleSessions.add(jingleSession);
jingleSession.addListener(this);
for (CreatedJingleSessionListener createdJingleSessionListener : creationListeners) {
try {
createdJingleSessionListener.sessionCreated(jingleSession);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) {
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
jingleSession.close();
LOGGER.severe("Declined:" + reason);
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
}
public void sessionClosed(String reason, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
}
public void sessionMediaReceived(JingleSession jingleSession, String participant) {
// Do Nothing
}
/**
* Register the listenerJingles, waiting for a Jingle packet that tries to
* establish a new session.
*/
private void initJingleSessionRequestListeners() {
PacketFilter initRequestFilter = new PacketFilter() {
// Return true if we accept this packet
public boolean accept(Packet pin) {
if (pin instanceof IQ) {
IQ iq = (IQ) pin;
if (iq.getType().equals(IQ.Type.set)) {
if (iq instanceof Jingle) {
Jingle jin = (Jingle) pin;
if (jin.getAction().equals(JingleActionEnum.SESSION_INITIATE)) {
return true;
}
}
}
}
return false;
}
};
jingleSessionRequestListeners = new ArrayList<JingleSessionRequestListener>();
// Start a packet listener for session initiation requests
connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
triggerSessionRequested((Jingle) packet);
}
}, initRequestFilter);
}
/**
* Disconnect all Jingle Sessions
*/
public void disconnectAllSessions() {
List<JingleSession> sessions = jingleSessions.subList(0, jingleSessions.size());
for (JingleSession jingleSession : sessions)
try {
jingleSession.terminate();
} catch (Exception e) {
e.printStackTrace();
}
sessions.clear();
}
/**
* Activates the listenerJingles on a Jingle session request.
*
* @param initJin the packet that must be passed to the jingleSessionRequestListener.
*/
void triggerSessionRequested(Jingle initJin) {
JingleSessionRequestListener[] jingleSessionRequestListeners = null;
// Make a synchronized copy of the listenerJingles
synchronized (this.jingleSessionRequestListeners) {
jingleSessionRequestListeners = new JingleSessionRequestListener[this.jingleSessionRequestListeners.size()];
this.jingleSessionRequestListeners.toArray(jingleSessionRequestListeners);
}
// ... and let them know of the event
JingleSessionRequest request = new JingleSessionRequest(this, initJin);
for (int i = 0; i < jingleSessionRequestListeners.length; i++) {
jingleSessionRequestListeners[i].sessionRequested(request);
}
}
// Session creation
/**
* Creates an Jingle session to start a communication with another user.
*
* @param responder the fully qualified jabber ID with resource of the other
* user.
* @return The session on which the negotiation can be run.
*/
public JingleSession createOutgoingJingleSession(String responder) throws XMPPException {
if (XmppStringUtils.isFullJID(responder)) {
throw new IllegalArgumentException("The provided user id was not fully qualified");
}
JingleSession session = new JingleSession(connection, (JingleSessionRequest) null, connection.getUser(), responder, jingleMediaManagers);
triggerSessionCreated(session);
return session;
}
/**
* Creates an Jingle session to start a communication with another user.
*
* @param responder the fully qualified jabber ID with resource of the other
* user.
* @return the session on which the negotiation can be run.
*/
// public OutgoingJingleSession createOutgoingJingleSession(String responder) throws XMPPException {
// if (this.getMediaManagers() == null) return null;
// return createOutgoingJingleSession(responder, this.getMediaManagers());
// }
/**
* When the session request is acceptable, this method should be invoked. It
* will create an JingleSession which allows the negotiation to procede.
*
* @param request the remote request that is being accepted.
* @return the session which manages the rest of the negotiation.
*/
public JingleSession createIncomingJingleSession(JingleSessionRequest request) throws XMPPException {
if (request == null) {
throw new NullPointerException("Received request cannot be null");
}
JingleSession session = new JingleSession(connection, request, request.getFrom(), connection.getUser(), jingleMediaManagers);
triggerSessionCreated(session);
return session;
}
/**
* When the session request is acceptable, this method should be invoked. It
* will create an JingleSession which allows the negotiation to procede.
* This method use JingleMediaManager to select the supported Payload types.
*
* @param request the remote request that is being accepted.
* @return the session which manages the rest of the negotiation.
*/
// IncomingJingleSession createIncomingJingleSession(JingleSessionRequest request) throws XMPPException {
// if (request == null) {
// throw new NullPointerException("JingleMediaManager is not defined");
// }
// if (jingleMediaManager != null)
// return createIncomingJingleSession(request, jingleMediaManager.getPayloads());
//
// return createIncomingJingleSession(request, null);
// }
/**
* Get a session with the informed JID. If no session is found, return null.
*
* @param jid
* @return the JingleSession
*/
public JingleSession getSession(String jid) {
for (JingleSession jingleSession : jingleSessions) {
if (jingleSession.getResponder().equals(jid)) {
return jingleSession;
}
}
return null;
}
}

View file

@ -0,0 +1,259 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.listeners.JingleListener;
/**
* Basic Jingle negotiator.
* <p/>
* </p>
* <p/>
* JingleNegotiator implements some basic behavior for every Jingle negotiation.
* It implements a "state" pattern: each stage should process Jingle packets and
* act depending on the current state in the negotiation...
* <p/>
* </p>
*
* @author Alvaro Saurin
* @author Jeff Williams
*/
public abstract class JingleNegotiator {
private static final Logger LOGGER = Logger.getLogger(JingleNegotiator.class.getName());
//private XMPPConnection connection; // The connection associated
protected JingleSession session;
private final List<JingleListener> listeners = new ArrayList<JingleListener>();
private String expectedAckId;
private JingleNegotiatorState state;
private boolean isStarted;
/**
* Default constructor.
*/
public JingleNegotiator() {
this(null);
}
/**
* Default constructor with a Connection
*
* @param session the jingle session
*/
public JingleNegotiator(JingleSession session) {
this.session = session;
state = JingleNegotiatorState.PENDING;
}
public JingleNegotiatorState getNegotiatorState() {
return state;
}
public void setNegotiatorState(JingleNegotiatorState stateIs) {
JingleNegotiatorState stateWas = state;
LOGGER.fine("Negotiator state change: " + stateWas + "->" + stateIs + "(" + this.getClass().getSimpleName() + ")");
switch (stateIs) {
case PENDING:
break;
case FAILED:
break;
case SUCCEEDED:
break;
default:
break;
}
this.state = stateIs;
}
public XMPPConnection getConnection() {
if (session != null) {
return session.getConnection();
} else {
return null;
}
}
/**
* Get the XMPP connection associated with this negotiation.
*
* @return the connection
*/
public JingleSession getSession() {
return session;
}
/**
* Set the XMPP connection associated.
*
* @param session the jingle session
*/
public void setSession(JingleSession session) {
this.session = session;
}
// Acks management
/**
* Add expected ID
*
* @param id
*/
public void addExpectedId(String id) {
expectedAckId = id;
}
/**
* Check if the passed ID is the expected ID
*
* @param id
* @return true if is expected id
*/
public boolean isExpectedId(String id) {
if (id != null) {
return id.equals(expectedAckId);
} else {
return false;
}
}
/**
* Remove and expected ID
*
* @param id
*/
public void removeExpectedId(String id) {
addExpectedId((String) null);
}
// Listeners
/**
* Add a Jingle session listener to listen to incoming session requests.
*
* @param li The listener
* @see org.jivesoftware.smackx.jingleold.listeners.JingleListener
*/
public void addListener(JingleListener li) {
synchronized (listeners) {
listeners.add(li);
}
}
/**
* Removes a Jingle session listener.
*
* @param li The jingle session listener to be removed
* @see org.jivesoftware.smackx.jingleold.listeners.JingleListener
*/
public void removeListener(JingleListener li) {
synchronized (listeners) {
listeners.remove(li);
}
}
/**
* Get a copy of the listeners
*
* @return a copy of the listeners
*/
protected List<JingleListener> getListenersList() {
ArrayList<JingleListener> result;
synchronized (listeners) {
result = new ArrayList<JingleListener>(listeners);
}
return result;
}
/**
* Dispatch an incoming packet.
*
* The negotiators form a tree relationship that roughly matches the Jingle packet format:
*
* JingleSession
* Content Negotiator
* Media Negotiator
* Transport Negotiator
* Content Negotiator
* Media Negotiator
* Transport Negotiator
*
* <jingle>
* <content>
* <description>
* <transport>
* <content>
* <description>
* <transport>
*
* This way, each segment of a Jingle packet has a corresponding negotiator that know how to deal with that
* part of the Jingle packet. It also allows us to support Jingle packets of arbitraty complexity.
*
* Each parent calls dispatchIncomingPacket for each of its children. The children then pass back a List<> of
* results that will get sent when we reach the top level negotiator (JingleSession).
*
* @param iq the packet received
* @param id the ID of the response that will be sent
* @return the new packet to send (either a Jingle or an IQ error).
* @throws XMPPException
*/
public abstract List<IQ> dispatchIncomingPacket(IQ iq, String id) throws XMPPException, SmackException;
public void start() {
isStarted = true;
doStart();
}
public boolean isStarted() {
return isStarted;
}
/**
* Each of the negotiators has their individual behavior when they start.
*/
protected abstract void doStart();
/**
* Close the negotiation.
*/
public void close() {
}
}

View file

@ -0,0 +1,26 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold;
/**
* @author Jeff Williams
*/
public enum JingleNegotiatorState {
PENDING,
FAILED,
SUCCEEDED
}

View file

@ -0,0 +1,142 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
/**
* A Jingle session request.
* <p/>
* This class is a facade of a received Jingle request. The user can have direct
* access to the Jingle packet (<i>JingleSessionRequest.getJingle() </i>) of
* the request or can use the convencience methods provided by this class.
*
* @author Alvaro Saurin
*/
public class JingleSessionRequest {
private static final Logger LOGGER = Logger.getLogger(JingleSessionRequest.class.getName());
private final Jingle jingle; // The Jingle packet
private final JingleManager manager; // The manager associated to this
// request
/**
* A recieve request is constructed from the Jingle Initiation request
* received from the initator.
*
* @param manager The manager handling this request
* @param jingle The jingle IQ recieved from the initiator.
*/
public JingleSessionRequest(JingleManager manager, Jingle jingle) {
this.manager = manager;
this.jingle = jingle;
}
/**
* Returns the fully-qualified jabber ID of the user that requested this
* session.
*
* @return Returns the fully-qualified jabber ID of the user that requested
* this session.
*/
public String getFrom() {
return jingle.getFrom();
}
/**
* Returns the session ID that uniquely identifies this session.
*
* @return Returns the session ID that uniquely identifies this session
*/
public String getSessionID() {
return jingle.getSid();
}
/**
* Returns the Jingle packet that was sent by the requester which contains
* the parameters of the session.
*/
public Jingle getJingle() {
return jingle;
}
/**
* Accepts this request and creates the incoming Jingle session.
*
* @param pts list of supported Payload Types
* @return Returns the <b><i>IncomingJingleSession</b></i> on which the
* negotiation can be carried out.
*/
// public synchronized JingleSession accept(List<PayloadType> pts) throws XMPPException {
// JingleSession session = null;
// synchronized (manager) {
// session = manager.createIncomingJingleSession(this, pts);
// // Acknowledge the IQ reception
// session.setSid(this.getSessionID());
// //session.sendAck(this.getJingle());
// //session.respond(this.getJingle());
// }
// return session;
// }
/**
* Accepts this request and creates the incoming Jingle session.
*
* @return Returns the <b><i>IncomingJingleSession</b></i> on which the
* negotiation can be carried out.
* @throws SmackException
*/
public synchronized JingleSession accept() throws XMPPException, SmackException {
JingleSession session = null;
synchronized (manager) {
session = manager.createIncomingJingleSession(this);
// Acknowledge the IQ reception
session.setSid(this.getSessionID());
//session.sendAck(this.getJingle());
session.updatePacketListener();
session.receivePacketAndRespond(this.getJingle());
}
return session;
}
/**
* Rejects the session request.
*/
public synchronized void reject() {
JingleSession session = null;
synchronized (manager) {
try {
session = manager.createIncomingJingleSession(this);
// Acknowledge the IQ reception
session.setSid(this.getSessionID());
//session.sendAck(this.getJingle());
session.updatePacketListener();
session.terminate("Declined");
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Exception in reject", e);
}
}
}
}

View file

@ -0,0 +1,66 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
/**
* Implement the Jingle Session state using the State Behavioral pattern.
* (From the book Design Patterns, AKA GoF.)
* These classes also employ the Flyweight and Singleton patterns as recommended for the State pattern by GoF.
*
* There seems to be three ways to go with the State pattern in Java: interface, abstract class and enums.
* Most of the accepted models use abstract classes. It wasn't clear to me that any of the three models was
* superior, so I went with the most common example.
*
* @author Jeff Williams
*/
public abstract class JingleSessionState {
/**
* Called when entering the state.
*/
public static JingleSessionState getInstance() {
// Since we can never instantiate this class there is nothing to return (ever).
return null;
}
/**
* Called when entering the state.
*/
public abstract void enter();
/**
* Called when exiting the state.
*/
public abstract void exit();
/**
* Process an incoming Jingle Packet.
* When you look at the GoF State pattern this method roughly corresponds to example on p310: ProcessOctect().
*/
public abstract IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) throws SmackException;
/**
* For debugging just emit the short name of the class.
*/
public String toString() {
return this.getClass().getSimpleName();
}
}

View file

@ -0,0 +1,107 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
import org.jivesoftware.smackx.jingleold.packet.JingleError;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStateActive extends JingleSessionState {
private static JingleSessionStateActive INSTANCE = null;
protected JingleSessionStateActive() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStateActive();
}
return INSTANCE;
}
public void enter() {
// TODO Auto-generated method stub
}
public void exit() {
// TODO Auto-generated method stub
}
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) {
IQ response = null;
switch (action) {
case CONTENT_ACCEPT:
break;
case CONTENT_ADD:
break;
case CONTENT_MODIFY:
break;
case CONTENT_REMOVE:
break;
case SESSION_INFO:
break;
case SESSION_TERMINATE:
receiveSessionTerminateAction(session, jingle);
break;
case TRANSPORT_INFO:
break;
default:
// Anything other action is an error.
response = session.createJingleError(jingle, JingleError.OUT_OF_ORDER);
break;
}
return response;
}
/**
* Receive and process the <session-terminate> action.
*/
private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
// According to XEP-166 the only thing we can do is ack.
IQ response = session.createAck(jingle);
try {
session.terminate("Closed remotely");
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
}

View file

@ -0,0 +1,72 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
import java.util.logging.Logger;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
import org.jivesoftware.smackx.jingleold.packet.JingleError;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStateEnded extends JingleSessionState {
private static final Logger LOGGER = Logger.getLogger(JingleSessionStateEnded.class.getName());
private static JingleSessionStateEnded INSTANCE = null;
protected JingleSessionStateEnded() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStateEnded();
}
return INSTANCE;
}
public void enter() {
LOGGER.fine("Session Ended");
LOGGER.fine("-------------------------------------------------------------------");
}
public void exit() {
// TODO Auto-generated method stub
}
/**
* Pretty much nothing is valid for receiving once we've ended the session.
*/
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) {
IQ response = null;
response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA);
return response;
}
}

View file

@ -0,0 +1,132 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStatePending extends JingleSessionState {
private static JingleSessionStatePending INSTANCE = null;
protected JingleSessionStatePending() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStatePending();
}
return INSTANCE;
}
public void enter() {
// TODO Auto-generated method stub
}
public void exit() {
// TODO Auto-generated method stub
}
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) {
IQ response = null;
switch (action) {
case CONTENT_ACCEPT:
response = receiveContentAcceptAction(jingle);
break;
case CONTENT_MODIFY:
break;
case CONTENT_REMOVE:
break;
case SESSION_ACCEPT:
response = receiveSessionAcceptAction(session, jingle);
break;
case SESSION_INFO:
break;
case SESSION_TERMINATE:
response = receiveSessionTerminateAction(session, jingle);
break;
case TRANSPORT_INFO:
break;
default:
// Anything other action is an error.
//response = createJingleError(inJingle, JingleError.OUT_OF_ORDER);
break;
}
return response;
}
/**
* Receive and process the <session-accept> action.
*/
private IQ receiveContentAcceptAction(Jingle inJingle) {
// According to XEP-167 the only thing we can do is ack.
//setSessionState(JingleSessionStateEnum.ACTIVE);
//return createAck(inJingle);
// This is now handled by the media negotiator for the matching <content> segment.
return null;
}
/**
* Receive and process the <session-accept> action.
*/
private IQ receiveSessionAcceptAction(JingleSession session, Jingle inJingle) {
// According to XEP-166 the only thing we can do is ack.
session.setSessionState(JingleSessionStateActive.getInstance());
return session.createAck(inJingle);
}
/**
* Receive and process the <session-terminate> action.
*/
private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
// According to XEP-166 the only thing we can do is ack.
IQ response = session.createAck(jingle);
try {
session.terminate("Closed remotely");
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
}

View file

@ -0,0 +1,213 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.MediaNegotiator;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportNegotiator;
import org.jivesoftware.smackx.jingleold.nat.TransportResolver;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
import org.jivesoftware.smackx.jingleold.packet.JingleContent;
import org.jivesoftware.smackx.jingleold.packet.JingleDescription;
import org.jivesoftware.smackx.jingleold.packet.JingleError;
import org.jivesoftware.smackx.jingleold.packet.JingleTransport;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStateUnknown extends JingleSessionState {
private static JingleSessionStateUnknown INSTANCE = null;
protected JingleSessionStateUnknown() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStateUnknown();
}
return INSTANCE;
}
public void enter() {
// TODO Auto-generated method stub
}
public void exit() {
// TODO Auto-generated method stub
}
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) throws SmackException {
IQ response = null;
switch (action) {
case SESSION_INITIATE:
response = receiveSessionInitiateAction(session, jingle);
break;
case SESSION_TERMINATE:
response = receiveSessionTerminateAction(session, jingle);
break;
default:
// Anything other than session-initiate is an error.
response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA);
break;
}
return response;
}
/**
* In the UNKNOWN state we received a <session-initiate> action.
* This method processes that action.
* @throws SmackException
*/
private IQ receiveSessionInitiateAction(JingleSession session, Jingle inJingle) throws SmackException {
IQ response = null;
boolean shouldAck = true;
// According to XEP-166 when we get a session-initiate we need to check for:
// 1. Initiator unknown
// 2. Receiver redirection
// 3. Does not support Jingle
// 4. Does not support any <description> formats
// 5. Does not support any <transport> formats
// If all of the above are OK then we send an IQ type = result to ACK the session-initiate.
// 1. Initiator unknown
// TODO
// 2. Receiver redirection
// TODO
// 3. Does not support Jingle
// Handled by Smack's lower layer.
// 4. Does not support any <description> formats
// TODO
// 5. Does not support any <transport> formats
// TODO
if (!shouldAck) {
response = session.createJingleError(inJingle, JingleError.NEGOTIATION_ERROR);
} else {
// Create the Ack
response = session.createAck(inJingle);
session.setSessionState(JingleSessionStatePending.getInstance());
// Now set up all of the initial content negotiators for the session.
for (JingleContent jingleContent : inJingle.getContentsList()) {
// First create the content negotiator for this <content> section.
ContentNegotiator contentNeg = new ContentNegotiator(session, jingleContent.getCreator(), jingleContent
.getName());
// Get the media negotiator that goes with the <description> of this content.
JingleDescription jingleDescription = jingleContent.getDescription();
// Loop through each media manager looking for the ones that matches the incoming
// session-initiate <content> choices.
// (Set the first media manager as the default, so that in case things don't match we can still negotiate.)
JingleMediaManager chosenMediaManager = session.getMediaManagers().get(0);
for (JingleMediaManager mediaManager : session.getMediaManagers()) {
boolean matches = true;
for (PayloadType mediaPayloadType : mediaManager.getPayloads()) {
for (PayloadType descPayloadType2 : jingleDescription.getPayloadTypesList()) {
if (mediaPayloadType.getId() != descPayloadType2.getId()) {
matches = false;
}
}
if (matches) {
chosenMediaManager = mediaManager;
}
}
}
// Create the media negotiator for this content description.
contentNeg.setMediaNegotiator(new MediaNegotiator(session, chosenMediaManager, jingleDescription
.getPayloadTypesList(), contentNeg));
// For each transport type in this content, try to find the corresponding transport manager.
// Then create a transport negotiator for that transport.
for (JingleTransport jingleTransport : jingleContent.getJingleTransportsList()) {
for (JingleMediaManager mediaManager : session.getMediaManagers()) {
JingleTransportManager transportManager = mediaManager.getTransportManager();
TransportResolver resolver = null;
try {
resolver = transportManager.getResolver(session);
} catch (XMPPException e) {
e.printStackTrace();
}
if (resolver.getType().equals(TransportResolver.Type.rawupd)) {
contentNeg.setTransportNegotiator(new TransportNegotiator.RawUdp(session, resolver, contentNeg));
}
if (resolver.getType().equals(TransportResolver.Type.ice)) {
contentNeg.setTransportNegotiator(new TransportNegotiator.Ice(session, resolver, contentNeg));
}
}
}
// Add the content negotiator to the session.
session.addContentNegotiator(contentNeg);
}
// Now setup to track the media negotiators, so that we know when (if) to send a session-accept.
session.setupListeners();
}
return response;
}
/**
* Receive and process the <session-terminate> action.
*/
private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
// According to XEP-166 the only thing we can do is ack.
IQ response = session.createAck(jingle);
try {
session.terminate("Closed remotely");
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
}

View file

@ -0,0 +1,30 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.listeners;
import org.jivesoftware.smackx.jingleold.JingleSession;
/**
* Inteface used to dispatch a event when a Jingle session is created.
*
* @author Thiago Camargo
*/
public interface CreatedJingleSessionListener {
public void sessionCreated(JingleSession jingleSession);
}

View file

@ -0,0 +1,37 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.listeners;
/**
* Jingle listeners interface.
*
* This is the list of events that can be observed from a JingleSession and some
* sub negotiators. This listeners can be added to different elements of the
* Jingle model.
*
* For example, a JingleManager can notify any SessionRequestListenerListener
* listener when a new session request is received. In this case, the
* <i>sessionRequested()</i> of the listener will be executed, and the listener
* will be able to <i>accept()</i> or <i>decline()</i> the invitation.
*
* @author Thiago Camargo
*/
public interface JingleListener {
}

View file

@ -0,0 +1,48 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.listeners;
/**
* Interface for listening to jmf info events.
* @author Thiago Camargo
*/
public interface JingleMediaInfoListener extends JingleListener {
/**
* The other end is busy.
*/
public void mediaInfoBusy();
/**
* We are on hold.
*/
public void mediaInfoHold();
/**
* The jmf is muted.
*/
public void mediaInfoMute();
/**
* We are queued.
*/
public void mediaInfoQueued();
/**
* We are ringing.
*/
public void mediaInfoRinging();
}

View file

@ -0,0 +1,42 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.listeners;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
/**
* Interface for listening to jmf events.
* @author Thiago Camargo
*/
public interface JingleMediaListener extends JingleListener {
/**
* Notification that the jmf has been negotiated and established.
*
* @param pt The payload type agreed.
* @throws NotConnectedException
*/
public void mediaEstablished(PayloadType pt) throws NotConnectedException;
/**
* Notification that a payload type must be cancelled
*
* @param cand The payload type that must be closed
*/
public void mediaClosed(PayloadType cand);
}

View file

@ -0,0 +1,84 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.listeners;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* Interface for listening for session events.
* @author Thiago Camargo
*/
public interface JingleSessionListener extends JingleListener {
/**
* Notification that the session has been established. Arguments specify
* the payload type and transport to use.
*
* @param pt the Payload tyep to use
* @param remoteCandidate the remote candidate to use for connecting to the remote
* service.
* @param localCandidate the local candidate where we must listen for connections
* @param jingleSession Session that called the method
* @throws NotConnectedException
*/
public void sessionEstablished(PayloadType pt, TransportCandidate remoteCandidate,
TransportCandidate localCandidate, JingleSession jingleSession) throws NotConnectedException;
/**
* Notification that the session was declined.
*
* @param reason the reason (if any).
* @param jingleSession Session that called the method
*/
public void sessionDeclined(String reason, JingleSession jingleSession);
/**
* Notification that the session was redirected.
*
* @param redirection
* @param jingleSession session that called the method
*/
public void sessionRedirected(String redirection, JingleSession jingleSession);
/**
* Notification that the session was closed normally.
*
* @param reason the reason (if any).
* @param jingleSession Session that called the method
*/
public void sessionClosed(String reason, JingleSession jingleSession);
/**
* Notification that the session was closed due to an exception.
*
* @param e the exception.
* @param jingleSession session that called the method
*/
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession);
/**
* Notification that the Media has arrived for this session.
*
* @param jingleSession session that called the method
* @param participant description of the participant
*/
public void sessionMediaReceived(JingleSession jingleSession, String participant);
}

View file

@ -0,0 +1,33 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.listeners;
import org.jivesoftware.smackx.jingleold.JingleSessionRequest;
/**
* Interface to listener Jingle session requests.
*
* @author Alvaro Saurin
*/
public interface JingleSessionRequestListener extends JingleListener {
/**
* A request to start a session has been recieved from another user.
*
* @param request The request from the other user.
*/
public void sessionRequested(JingleSessionRequest request);
}

View file

@ -0,0 +1,57 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.listeners;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* Interface for listening to transport events.
*
* @author Thiago Camargo
*/
public interface JingleTransportListener extends JingleListener {
/**
* Notification that the transport has been established.
*
* @param local The transport candidate that has been used for listening
* in the local machine
* @param remote The transport candidate that has been used for
* transmitting to the remote machine
* @throws NotConnectedException
*/
public void transportEstablished(TransportCandidate local,
TransportCandidate remote) throws NotConnectedException;
/**
* Notification that a transport must be cancelled.
*
* @param cand The transport candidate that must be cancelled. A value
* of "null" means all the transports for this session.
*/
public void transportClosed(TransportCandidate cand);
/**
* Notification that the transport was closed due to an exception.
*
* @param e the exception.
*/
public void transportClosedOnError(XMPPException e);
}

View file

@ -0,0 +1,76 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.media;
import java.util.Locale;
/**
* Content info. Content info messages are complementary messages that can be
* transmitted for informing of events like "busy", "ringtone", etc.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public abstract class ContentInfo {
/**
* Audio content info messages.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public static class Audio extends ContentInfo {
public static final ContentInfo.Audio BUSY = new ContentInfo.Audio("busy");
public static final ContentInfo.Audio HOLD = new ContentInfo.Audio("hold");
public static final ContentInfo.Audio MUTE = new ContentInfo.Audio("mute");
public static final ContentInfo.Audio QUEUED = new ContentInfo.Audio("queued");
public static final ContentInfo.Audio RINGING = new ContentInfo.Audio("ringing");
private String value;
public Audio(String value) {
this.value = value;
}
public String toString() {
return value;
}
/**
* Returns the MediaInfo constant associated with the String value.
*/
public static ContentInfo fromString(String value) {
value = value.toLowerCase(Locale.US);
if (value.equals("busy")) {
return BUSY;
} else if (value.equals("hold")) {
return HOLD;
} else if (value.equals("mute")) {
return MUTE;
} else if (value.equals("queued")) {
return QUEUED;
} else if (value.equals("ringing")) {
return RINGING;
} else {
return null;
}
}
}
}

View file

@ -0,0 +1,85 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.media;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
import java.util.List;
/**
* This class provides necessary Jingle Session jmf methods and behavior.
* <p/>
* The goal of this class is to provide a flexible way to make JingleManager control jmf streaming APIs without implement them.
* For instance you can implement a file transfer using java sockets or a VOIP Media Manager using JMF.
* You can implement many JingleMediaManager according to you necessity.
*
* @author Thiago Camargo
*/
public abstract class JingleMediaManager {
public static final String MEDIA_NAME = "JingleMediaManager";
// Each media manager must keep track of the transport manager that it uses.
private JingleTransportManager transportManager;
public JingleMediaManager(JingleTransportManager transportManager) {
this.transportManager = transportManager;
}
/**
* Return The transport manager that goes with this media manager.
*/
public JingleTransportManager getTransportManager() {
return transportManager;
}
/**
* Return all supported Payloads for this Manager
*
* @return The Payload List
*/
public abstract List<PayloadType> getPayloads();
/**
* Returns the Preferred PayloadType of the Media Manager
*
* @return The PayloadType
*/
public PayloadType getPreferredPayloadType() {
return getPayloads().size() > 0 ? getPayloads().get(0) : null;
}
/**
* Create a Media Session Implementation
*
* @param payloadType
* @param remote
* @param local
* @return the media session
*/
public abstract JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote,
final TransportCandidate local, JingleSession jingleSession);
// This is to set the attributes of the <content> element of the Jingle packet.
public String getName() {
return MEDIA_NAME;
}
}

View file

@ -0,0 +1,188 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.media;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
import java.util.ArrayList;
import java.util.List;
/**
* Public Abstract Class provides a clear interface between Media Session and Jingle API.
* <p/>
* When a Jingle Session is fully stablished, we will have a Payload Type and two transport candidates defined for it.
* Smack Jingle API don't implement Media Transmit and Receive methods.
* But provides an interface to let the user implements it using another API. For instance: JMF.
* <p/>
* <i>The Class that implements this one, must have the support to transmit and receive the jmf.</i>
* <i>This interface let the user choose his own jmf API.</i>
*
* @author Thiago Camargo
*/
public abstract class JingleMediaSession {
// Payload Type of the Session
private PayloadType payloadType;
// Local Transport details
private TransportCandidate local;
// Remote Transport details
private TransportCandidate remote;
// Media Locator
private String mediaLocator;
// Media Received Listener
private List<MediaReceivedListener> mediaReceivedListeners = new ArrayList<MediaReceivedListener>();
// Jingle Session
private JingleSession jingleSession;
/**
* Creates a new JingleMediaSession Instance to handle Media methods.
*
* @param payloadType Payload Type of the transmittion
* @param remote Remote accepted Transport Candidate
* @param local Local accepted Transport Candidate
* @param mediaLocator Media Locator of the capture device
*/
public JingleMediaSession(PayloadType payloadType, TransportCandidate remote,
TransportCandidate local, String mediaLocator, JingleSession jingleSession) {
this.local = local;
this.remote = remote;
this.payloadType = payloadType;
this.mediaLocator = mediaLocator;
this.jingleSession = jingleSession;
}
/**
* Returns the PayloadType of the Media Session
*
* @return the PayloadType
*/
public PayloadType getPayloadType() {
return payloadType;
}
/**
* Returns the Media Session local Candidate
*
* @return the TransportCandidate
*/
public TransportCandidate getLocal() {
return local;
}
/**
* Returns the Media Session remote Candidate
*
* @return the TransportCandidate
*/
public TransportCandidate getRemote() {
return remote;
}
/**
* Return the media locator or null if not defined
*
* @return media locator
*/
public String getMediaLocator() {
return mediaLocator;
}
/**
* Set the media locator
*
* @param mediaLocator media locator or null to use default
*/
public void setMediaLocator(String mediaLocator) {
this.mediaLocator = mediaLocator;
}
/**
* Adds a Media Received Listener
*
* @param mediaReceivedListener
*/
public void addMediaReceivedListener(MediaReceivedListener mediaReceivedListener) {
mediaReceivedListeners.add(mediaReceivedListener);
}
/**
* Removes a Media Received Listener
*
* @param mediaReceivedListener
*/
public void removeMediaReceivedListener(MediaReceivedListener mediaReceivedListener) {
mediaReceivedListeners.remove(mediaReceivedListener);
}
/**
* Removes all Media Received Listeners
*/
public void removeAllMediaReceivedListener() {
mediaReceivedListeners.clear();
}
/**
* Initialize the RTP Channel preparing to transmit and receive.
*/
public abstract void initialize();
/**
* Starts a RTP / UDP / TCP Transmission to the remote Candidate
*/
public abstract void startTrasmit();
/**
* Starts a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate
*/
public abstract void startReceive();
/**
* Set transmit activity. If the active is true, the instance should trasmit.
* If it is set to false, the instance should pause transmit.
*
* @param active
*/
public abstract void setTrasmit(boolean active);
/**
* Stops a RTP / UDP / TCP Transmission to the remote Candidate
*/
public abstract void stopTrasmit();
/**
* Stops a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate
*/
public abstract void stopReceive();
/**
* Called when new Media is received.
*/
public void mediaReceived(String participant) {
for (MediaReceivedListener mediaReceivedListener : mediaReceivedListeners) {
mediaReceivedListener.mediaReceived(participant);
}
}
/**
* Gets associated JingleSession
* @return associated JingleSession
*/
public JingleSession getJingleSession() {
return jingleSession;
}
}

View file

@ -0,0 +1,537 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold.media;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.ContentNegotiator;
import org.jivesoftware.smackx.jingleold.JingleActionEnum;
import org.jivesoftware.smackx.jingleold.JingleException;
import org.jivesoftware.smackx.jingleold.JingleNegotiator;
import org.jivesoftware.smackx.jingleold.JingleNegotiatorState;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.listeners.JingleListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleMediaListener;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
import org.jivesoftware.smackx.jingleold.packet.JingleContent;
import org.jivesoftware.smackx.jingleold.packet.JingleDescription;
import org.jivesoftware.smackx.jingleold.packet.JingleError;
/**
* Manager for jmf descriptor negotiation. <p/> <p/> This class is responsible
* for managing the descriptor negotiation process, handling all the xmpp
* packets interchange and the stage control. handling all the xmpp packets
* interchange and the stage control.
*
* @author Thiago Camargo
*/
public class MediaNegotiator extends JingleNegotiator {
private static final Logger LOGGER = Logger.getLogger(MediaNegotiator.class.getName());
//private JingleSession session; // The session this negotiation
private final JingleMediaManager mediaManager;
// Local and remote payload types...
private final List<PayloadType> localAudioPts = new ArrayList<PayloadType>();
private final List<PayloadType> remoteAudioPts = new ArrayList<PayloadType>();
private PayloadType bestCommonAudioPt;
private ContentNegotiator parentNegotiator;
/**
* Default constructor. The constructor establishes some basic parameters,
* but it does not start the negotiation. For starting the negotiation, call
* startNegotiation.
*
* @param session
* The jingle session.
*/
public MediaNegotiator(JingleSession session, JingleMediaManager mediaManager, List<PayloadType> pts,
ContentNegotiator parentNegotiator) {
super(session);
this.mediaManager = mediaManager;
this.parentNegotiator = parentNegotiator;
bestCommonAudioPt = null;
if (pts != null) {
if (pts.size() > 0) {
localAudioPts.addAll(pts);
}
}
}
/**
* Return The media manager for this negotiator.
*/
public JingleMediaManager getMediaManager() {
return mediaManager;
}
/**
* Dispatch an incoming packet. The method is responsible for recognizing
* the packet type and, depending on the current state, delivering the
* packet to the right event handler and wait for a response.
*
* @param iq
* the packet received
* @return the new Jingle packet to send.
* @throws XMPPException
* @throws NotConnectedException
*/
public List<IQ> dispatchIncomingPacket(IQ iq, String id) throws XMPPException, NotConnectedException {
List<IQ> responses = new ArrayList<IQ>();
IQ response = null;
if (iq.getType().equals(IQ.Type.error)) {
// Process errors
setNegotiatorState(JingleNegotiatorState.FAILED);
triggerMediaClosed(getBestCommonAudioPt());
// This next line seems wrong, and may subvert the normal closing process.
throw new JingleException(iq.getError().getMessage());
} else if (iq.getType().equals(IQ.Type.result)) {
// Process ACKs
if (isExpectedId(iq.getPacketID())) {
receiveResult(iq);
removeExpectedId(iq.getPacketID());
}
} else if (iq instanceof Jingle) {
Jingle jingle = (Jingle) iq;
JingleActionEnum action = jingle.getAction();
// Only act on the JingleContent sections that belong to this media negotiator.
for (JingleContent jingleContent : jingle.getContentsList()) {
if (jingleContent.getName().equals(parentNegotiator.getName())) {
JingleDescription description = jingleContent.getDescription();
if (description != null) {
switch (action) {
case CONTENT_ACCEPT:
response = receiveContentAcceptAction(jingle, description);
break;
case CONTENT_MODIFY:
break;
case CONTENT_REMOVE:
break;
case SESSION_INFO:
response = receiveSessionInfoAction(jingle, description);
break;
case SESSION_INITIATE:
response = receiveSessionInitiateAction(jingle, description);
break;
case SESSION_ACCEPT:
response = receiveSessionAcceptAction(jingle, description);
break;
default:
break;
}
}
}
}
}
if (response != null) {
addExpectedId(response.getPacketID());
responses.add(response);
}
return responses;
}
/**
* Process the ACK of our list of codecs (our offer).
*/
private Jingle receiveResult(IQ iq) throws XMPPException {
Jingle response = null;
// if (!remoteAudioPts.isEmpty()) {
// // Calculate the best common codec
// bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
//
// // and send an accept if we havee an agreement...
// if (bestCommonAudioPt != null) {
// response = createAcceptMessage();
// } else {
// throw new JingleException(JingleError.NO_COMMON_PAYLOAD);
// }
// }
return response;
}
/**
* The other side has sent us a content-accept. The payload types in that message may not match with what
* we sent, but XEP-167 says that the other side should retain the order of the payload types we first sent.
*
* This means we can walk through our list, in order, until we find one from their list that matches. This
* will be the best payload type to use.
*
* @param jingle
* @return the iq
* @throws NotConnectedException
*/
private IQ receiveContentAcceptAction(Jingle jingle, JingleDescription description) throws XMPPException, NotConnectedException {
IQ response = null;
List<PayloadType> offeredPayloads = new ArrayList<PayloadType>();
offeredPayloads = description.getAudioPayloadTypesList();
bestCommonAudioPt = calculateBestCommonAudioPt(offeredPayloads);
if (bestCommonAudioPt == null) {
setNegotiatorState(JingleNegotiatorState.FAILED);
response = session.createJingleError(jingle, JingleError.NEGOTIATION_ERROR);
} else {
setNegotiatorState(JingleNegotiatorState.SUCCEEDED);
triggerMediaEstablished(getBestCommonAudioPt());
LOGGER.severe("Media choice:" + getBestCommonAudioPt().getName());
response = session.createAck(jingle);
}
return response;
}
/**
* Receive a session-initiate packet.
* @param jingle
* @param description
* @return the iq
*/
private IQ receiveSessionInitiateAction(Jingle jingle, JingleDescription description) {
IQ response = null;
List<PayloadType> offeredPayloads = new ArrayList<PayloadType>();
offeredPayloads = description.getAudioPayloadTypesList();
bestCommonAudioPt = calculateBestCommonAudioPt(offeredPayloads);
synchronized (remoteAudioPts) {
remoteAudioPts.addAll(offeredPayloads);
}
// If there are suitable/matching payload types then accept this content.
if (bestCommonAudioPt != null) {
// Let thre transport negotiators sort-out connectivity and content-accept instead.
//response = createAudioPayloadTypesOffer();
setNegotiatorState(JingleNegotiatorState.PENDING);
} else {
// Don't really know what to send here. XEP-166 is not clear.
setNegotiatorState(JingleNegotiatorState.FAILED);
}
return response;
}
/**
* A content info has been received. This is done for publishing the
* list of payload types...
*
* @param jin
* The input packet
* @return a Jingle packet
* @throws JingleException
*/
private IQ receiveSessionInfoAction(Jingle jingle, JingleDescription description) throws JingleException {
IQ response = null;
PayloadType oldBestCommonAudioPt = bestCommonAudioPt;
List<PayloadType> offeredPayloads;
boolean ptChange = false;
offeredPayloads = description.getAudioPayloadTypesList();
if (!offeredPayloads.isEmpty()) {
synchronized (remoteAudioPts) {
remoteAudioPts.clear();
remoteAudioPts.addAll(offeredPayloads);
}
// Calculate the best common codec
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
if (bestCommonAudioPt != null) {
// and send an accept if we have an agreement...
ptChange = !bestCommonAudioPt.equals(oldBestCommonAudioPt);
if (oldBestCommonAudioPt == null || ptChange) {
//response = createAcceptMessage();
}
} else {
throw new JingleException(JingleError.NO_COMMON_PAYLOAD);
}
}
// Parse the Jingle and get the payload accepted
return response;
}
/**
* A jmf description has been accepted. In this case, we must save the
* accepted payload type and notify any listener...
*
* @param jin
* The input packet
* @return a Jingle packet
* @throws JingleException
*/
private IQ receiveSessionAcceptAction(Jingle jingle, JingleDescription description) throws JingleException {
IQ response = null;
PayloadType.Audio agreedCommonAudioPt;
List<PayloadType> offeredPayloads = new ArrayList<PayloadType>();
if (bestCommonAudioPt == null) {
// Update the best common audio PT
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
//response = createAcceptMessage();
}
offeredPayloads = description.getAudioPayloadTypesList();
if (!offeredPayloads.isEmpty()) {
if (offeredPayloads.size() == 1) {
agreedCommonAudioPt = (PayloadType.Audio) offeredPayloads.get(0);
if (bestCommonAudioPt != null) {
// If the accepted PT matches the best payload
// everything is fine
if (!agreedCommonAudioPt.equals(bestCommonAudioPt)) {
throw new JingleException(JingleError.NEGOTIATION_ERROR);
}
}
} else if (offeredPayloads.size() > 1) {
throw new JingleException(JingleError.MALFORMED_STANZA);
}
}
return response;
}
/**
* Return true if the content is negotiated.
*
* @return true if the content is negotiated.
*/
public boolean isEstablished() {
return getBestCommonAudioPt() != null;
}
/**
* Return true if the content is fully negotiated.
*
* @return true if the content is fully negotiated.
*/
public boolean isFullyEstablished() {
return (isEstablished() && ((getNegotiatorState() == JingleNegotiatorState.SUCCEEDED) || (getNegotiatorState() == JingleNegotiatorState.FAILED)));
}
// Payload types
private PayloadType calculateBestCommonAudioPt(List<PayloadType> remoteAudioPts) {
final ArrayList<PayloadType> commonAudioPtsHere = new ArrayList<PayloadType>();
final ArrayList<PayloadType> commonAudioPtsThere = new ArrayList<PayloadType>();
PayloadType result = null;
if (!remoteAudioPts.isEmpty()) {
commonAudioPtsHere.addAll(localAudioPts);
commonAudioPtsHere.retainAll(remoteAudioPts);
commonAudioPtsThere.addAll(remoteAudioPts);
commonAudioPtsThere.retainAll(localAudioPts);
if (!commonAudioPtsHere.isEmpty() && !commonAudioPtsThere.isEmpty()) {
if (session.getInitiator().equals(session.getConnection().getUser())) {
PayloadType.Audio bestPtHere = null;
PayloadType payload = mediaManager.getPreferredPayloadType();
if (payload != null && payload instanceof PayloadType.Audio)
if (commonAudioPtsHere.contains(payload))
bestPtHere = (PayloadType.Audio) payload;
if (bestPtHere == null)
for (PayloadType payloadType : commonAudioPtsHere)
if (payloadType instanceof PayloadType.Audio) {
bestPtHere = (PayloadType.Audio) payloadType;
break;
}
result = bestPtHere;
} else {
PayloadType.Audio bestPtThere = null;
for (PayloadType payloadType : commonAudioPtsThere)
if (payloadType instanceof PayloadType.Audio) {
bestPtThere = (PayloadType.Audio) payloadType;
break;
}
result = bestPtThere;
}
}
}
return result;
}
/**
* Adds a payload type to the list of remote payloads.
*
* @param pt
* the remote payload type
*/
public void addRemoteAudioPayloadType(PayloadType.Audio pt) {
if (pt != null) {
synchronized (remoteAudioPts) {
remoteAudioPts.add(pt);
}
}
}
// /**
// * Create an offer for the list of audio payload types.
// *
// * @return a new Jingle packet with the list of audio Payload Types
// */
// private Jingle createAudioPayloadTypesOffer() {
//
// JingleContent jingleContent = new JingleContent(parentNegotiator.getCreator(), parentNegotiator.getName());
// JingleDescription audioDescr = new JingleDescription.Audio();
//
// // Add the list of payloads for audio and create a
// // JingleDescription
// // where we announce our payloads...
// audioDescr.addAudioPayloadTypes(localAudioPts);
// jingleContent.setDescription(audioDescr);
//
// Jingle jingle = new Jingle(JingleActionEnum.CONTENT_ACCEPT);
// jingle.addContent(jingleContent);
//
// return jingle;
// }
// Predefined messages and Errors
/**
* Create an IQ "accept" message.
*/
// private Jingle createAcceptMessage() {
// Jingle jout = null;
//
// // If we have a common best codec, send an accept right now...
// jout = new Jingle(JingleActionEnum.CONTENT_ACCEPT);
// JingleContent content = new JingleContent(parentNegotiator.getCreator(), parentNegotiator.getName());
// content.setDescription(new JingleDescription.Audio(bestCommonAudioPt));
// jout.addContent(content);
//
// return jout;
// }
// Payloads
/**
* Get the best common codec between both parts.
*
* @return The best common PayloadType codec.
*/
public PayloadType getBestCommonAudioPt() {
return bestCommonAudioPt;
}
// Events
/**
* Trigger a session established event.
*
* @param bestPt
* payload type that has been agreed.
* @throws NotConnectedException
*/
protected void triggerMediaEstablished(PayloadType bestPt) throws NotConnectedException {
List<JingleListener> listeners = getListenersList();
for (JingleListener li : listeners) {
if (li instanceof JingleMediaListener) {
JingleMediaListener mli = (JingleMediaListener) li;
mli.mediaEstablished(bestPt);
}
}
}
/**
* Trigger a jmf closed event.
*
* @param currPt
* current payload type that is cancelled.
*/
protected void triggerMediaClosed(PayloadType currPt) {
List<JingleListener> listeners = getListenersList();
for (JingleListener li : listeners) {
if (li instanceof JingleMediaListener) {
JingleMediaListener mli = (JingleMediaListener) li;
mli.mediaClosed(currPt);
}
}
}
/**
* Called from above when starting a new session.
*/
protected void doStart() {
}
/**
* Terminate the jmf negotiator
*/
public void close() {
super.close();
triggerMediaClosed(getBestCommonAudioPt());
}
/**
* Create a JingleDescription that matches this negotiator.
*/
public JingleDescription getJingleDescription() {
JingleDescription result = null;
PayloadType payloadType = getBestCommonAudioPt();
if (payloadType != null) {
result = new JingleDescription.Audio(payloadType);
} else {
// If we haven't settled on a best payload type yet then just use the first one in our local list.
result = new JingleDescription.Audio();
result.addAudioPayloadTypes(localAudioPts);
}
return result;
}
}

View file

@ -0,0 +1,29 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.media;
/**
* Listener for new Incoming Media Streams
*/
public interface MediaReceivedListener {
/**
* Called when new Media is received.
*/
public void mediaReceived(String participant);
}

View file

@ -0,0 +1,365 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold.media;
/**
* Represents a payload type.
*
* @author Alvaro Saurin
*/
public class PayloadType {
public static final String NODENAME = "payload-type";
public static int MAX_FIXED_PT = 95;
public static int INVALID_PT = 65535;
private int id;
private String name;
private int channels;
/**
* Constructor with Id, name and number of channels
*
* @param id The identifier
* @param name A name
* @param channels The number of channels
*/
public PayloadType(int id, final String name, int channels) {
super();
this.id = id;
this.name = name;
this.channels = channels;
}
/**
* Default constructor.
*/
public PayloadType() {
this(INVALID_PT, null, 1);
}
/**
* Constructor with Id and name
*
* @param id The identification
* @param name A name
*/
public PayloadType(int id, String name) {
this(id, name, 1);
}
/**
* Copy constructor
*
* @param pt The other payload type.
*/
public PayloadType(PayloadType pt) {
this(pt.getId(), pt.getName(), pt.getChannels());
}
/**
* Get the ID.
*
* @return the ID
*/
public int getId() {
return id;
}
/**
* Set the ID.
*
* @param id ID
*/
public void setId(int id) {
this.id = id;
}
/**
* Get the printable name.
*
* @return printable name for the payload type
*/
public String getName() {
return name;
}
/**
* Set the printable name.
*
* @param name the printable name
*/
public void setName(String name) {
this.name = name;
}
/**
* Get the number of channels used by this payload type.
*
* @return the number of channels
*/
public int getChannels() {
return channels;
}
/**
* Set the numer of channels for a payload type.
*
* @param channels The number of channels
*/
public void setChannels(int channels) {
this.channels = channels;
}
/**
* Return true if the Payload type is not valid
*
* @return true if the payload type is invalid
*/
public boolean isNull() {
if (getId() == INVALID_PT) {
return true;
}
else if (getName() == null) {
return true;
}
return false;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + getChannels();
result = PRIME * result + getId();
result = PRIME * result + (getName() == null ? 0 : getName().hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PayloadType other = (PayloadType) obj;
if (getChannels() != other.getChannels()) {
return false;
}
if (getId() != other.getId()) {
return false;
}
// Compare names only for dynamic payload types
if (getId() > MAX_FIXED_PT) {
if (getName() == null) {
if (other.getName() != null) {
return false;
}
}
else if (!getName().equals(other.getName())) {
return false;
}
}
return true;
}
/**
* Returns the XML element name of the element.
*
* @return the XML element name of the element.
*/
public static String getElementName() {
return NODENAME;
}
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName()).append(" ");
// We covert here the payload type to XML
if (this.getId() != PayloadType.INVALID_PT) {
buf.append(" id=\"").append(this.getId()).append("\"");
}
if (this.getName() != null) {
buf.append(" name=\"").append(this.getName()).append("\"");
}
if (this.getChannels() != 0) {
buf.append(" channels=\"").append(this.getChannels()).append("\"");
}
if (getChildAttributes() != null) {
buf.append(getChildAttributes());
}
buf.append("/>");
return buf.toString();
}
protected String getChildAttributes() {
StringBuilder buf = new StringBuilder();
if (this instanceof PayloadType.Audio) {
PayloadType.Audio pta = (PayloadType.Audio) this;
buf.append(" clockrate=\"").append(pta.getClockRate()).append("\" ");
}
return buf.toString();
}
/**
* Audio payload type.
*/
public static class Audio extends PayloadType {
private int clockRate;
/**
* Constructor with all the attributes of an Audio payload type
*
* @param id The identifier
* @param name The name assigned to this payload type
* @param channels The number of channels
* @param rate The clock rate
*/
public Audio(int id, String name, int channels, int rate) {
super(id, name, channels);
clockRate = rate;
}
/**
* Constructor with all the attributes of an Audio payload type
*
* @param id The identifier
* @param name The name assigned to this payload type
* @param rate The clock rate
*/
public Audio(int id, String name, int rate) {
super(id, name);
clockRate = rate;
}
/**
* Empty constructor.
*/
public Audio() {
super();
clockRate = 0;
}
/**
* Constructor with Id and name
*
* @param id the Id for the payload type
* @param name the name of the payload type
*/
public Audio(int id, String name) {
super(id, name);
clockRate = 0;
}
/**
* Copy constructor
*
* @param pt the other payload type
*/
public Audio(PayloadType pt) {
super(pt);
clockRate = 0;
}
/**
* Copy constructor
*
* @param pt the other payload type
*/
public Audio(PayloadType.Audio pt) {
super(pt);
clockRate = pt.getClockRate();
}
/**
* Get the sampling clockRate for a payload type
*
* @return The sampling clockRate
*/
public int getClockRate() {
return clockRate;
}
/**
* Set tha sampling clockRate for a playload type.
*
* @param rate The sampling clockRate
*/
public void setClockRate(int rate) {
clockRate = rate;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int PRIME = 31;
int result = super.hashCode();
result = PRIME * result + getClockRate();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Audio other = (Audio) obj;
if (getClockRate() != other.getClockRate()) {
return false;
}
return true;
}
}
}

View file

@ -0,0 +1,299 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.mediaimpl;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.Toolkit;
import java.util.Vector;
import java.util.logging.Logger;
import javax.media.Format;
import javax.media.PlugInManager;
import javax.media.Renderer;
import javax.media.format.AudioFormat;
import com.sun.media.ExclusiveUse;
import com.sun.media.util.Registry;
public class JMFInit extends Frame implements Runnable {
private static final long serialVersionUID = 6476412003260641680L;
private static final Logger LOGGER = Logger.getLogger(JMFInit.class.getName());
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<String> 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<String> 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) {
LOGGER.fine(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);
}
}

View file

@ -0,0 +1,549 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.jmf;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.media.Codec;
import javax.media.Controller;
import javax.media.ControllerClosedEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.Format;
import javax.media.MediaLocator;
import javax.media.NoProcessorException;
import javax.media.Processor;
import javax.media.UnsupportedPlugInException;
import javax.media.control.BufferControl;
import javax.media.control.PacketSizeControl;
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.InvalidSessionAddressException;
import javax.media.rtp.RTPManager;
import javax.media.rtp.SendStream;
import javax.media.rtp.SessionAddress;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
/**
* 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 static final Logger LOGGER = Logger.getLogger(AudioChannel.class.getName());
private MediaLocator locator;
private String localIpAddress;
private String remoteIpAddress;
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 JingleMediaSession jingleMediaSession;
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 remoteIpAddress remote IP address
* @param localPort local port number
* @param remotePort remote port number
* @param format audio format
*/
public AudioChannel(MediaLocator locator,
String localIpAddress,
String remoteIpAddress,
int localPort,
int remotePort,
Format format, JingleMediaSession jingleMediaSession) {
this.locator = locator;
this.localIpAddress = localIpAddress;
this.remoteIpAddress = remoteIpAddress;
this.localPort = localPort;
this.portBase = remotePort;
this.format = format;
this.jingleMediaSession = jingleMediaSession;
}
/**
* 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;
// Create a processor for the specified jmf locator
String result = createProcessor();
if (result != null) {
started = false;
}
// 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;
}
else {
started = true;
}
// 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) {
// Try JavaSound Locator as a last resort
try {
ds = javax.media.Manager.createDataSource(new MediaLocator("javasound://"));
}
catch (Exception ee) {
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);
LOGGER.severe("Track " + i + " is set to transmit as: " + chosen);
if (tracks[i].getFormat() instanceof AudioFormat) {
int packetRate = 20;
PacketSizeControl pktCtrl = (PacketSizeControl) processor.getControl(PacketSizeControl.class.getName());
if (pktCtrl != null) {
try {
pktCtrl.setPacketSize(getPacketSize(tracks[i].getFormat(), packetRate));
}
catch (IllegalArgumentException e) {
pktCtrl.setPacketSize(80);
// Do nothing
}
}
if (tracks[i].getFormat().getEncoding().equals(AudioFormat.ULAW_RTP)) {
Codec[] codec = new Codec[3];
codec[0] = new com.ibm.media.codec.audio.rc.RCModule();
codec[1] = new com.ibm.media.codec.audio.ulaw.JavaEncoder();
codec[2] = new com.sun.media.codec.audio.ulaw.Packetizer();
((com.sun.media.codec.audio.ulaw.Packetizer) codec
[2]).setPacketSize(160);
try {
tracks[i].setCodecChain(codec);
}
catch (UnsupportedPlugInException e) {
e.printStackTrace();
}
}
}
atLeastOneTrack = true;
}
else
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;
}
/**
* Get the best packet size for a given codec and a codec rate
*
* @param codecFormat
* @param milliseconds
* @return the best packet size
* @throws IllegalArgumentException
*/
private int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException {
String encoding = codecFormat.getEncoding();
if (encoding.equalsIgnoreCase(AudioFormat.GSM) ||
encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) {
return milliseconds * 4; // 1 byte per millisec
}
else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) ||
encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) {
return milliseconds * 8;
}
else {
throw new IllegalArgumentException("Unknown codec type");
}
}
/**
* Use the RTPManager API to create sessions for each jmf
* 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, jingleMediaSession);
int port;
for (int i = 0; i < pbss.length; i++) {
try {
rtpMgrs[i] = RTPManager.newInstance();
port = portBase + 2 * i;
ipAddr = InetAddress.getByName(remoteIpAddress);
localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress),
localPort);
destAddr = new SessionAddress(ipAddr, port);
rtpMgrs[i].addReceiveStreamListener(audioReceiver);
rtpMgrs[i].addSessionListener(audioReceiver);
BufferControl bc = (BufferControl) rtpMgrs[i].getControl("javax.media.control.BufferControl");
if (bc != null) {
int bl = 160;
bc.setBufferLength(bl);
}
try {
rtpMgrs[i].initialize(localAddr);
}
catch (InvalidSessionAddressException e) {
// In case the local address is not allowed to read, we user another local address
SessionAddress sessAddr = new SessionAddress();
localAddr = new SessionAddress(sessAddr.getDataAddress(),
localPort);
rtpMgrs[i].initialize(localAddr);
}
rtpMgrs[i].addTarget(destAddr);
LOGGER.severe("Created RTP session at " + localPort + " to: " + remoteIpAddress + " " + 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();
LOGGER.fine("START");
}
else {
sendStream.stop();
LOGGER.fine("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), null);
AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP), null);
audioChannel0.start();
audioChannel1.start();
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.setTrasmit(false);
audioChannel1.setTrasmit(false);
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.setTrasmit(true);
audioChannel1.setTrasmit(true);
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.stop();
audioChannel1.stop();
}
catch (UnknownHostException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,52 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.jmf;
import org.jivesoftware.smackx.jingleold.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;
}
}
}

View file

@ -0,0 +1,161 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.jmf;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.logging.Logger;
import javax.media.MediaLocator;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* 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 static final Logger LOGGER = Logger.getLogger(AudioMediaSession.class.getName());
private AudioChannel audioChannel;
/**
* Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
*
* @param payloadType Payload of the jmf
* @param remote the remote information. The candidate that the jmf will be sent to.
* @param local the local information. The candidate that will receive the jmf
* @param locator media locator
*/
public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
final TransportCandidate local, String locator, JingleSession jingleSession) {
super(payloadType, remote, local, locator==null?"dsound://":locator,jingleSession);
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();
LOGGER.fine(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(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()),this);
}
/**
* 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;
}
}

View file

@ -0,0 +1,168 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.jmf;
import java.util.logging.Logger;
import javax.media.ControllerErrorEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.Player;
import javax.media.RealizeCompleteEvent;
import javax.media.protocol.DataSource;
import javax.media.rtp.Participant;
import javax.media.rtp.RTPControl;
import javax.media.rtp.ReceiveStream;
import javax.media.rtp.ReceiveStreamListener;
import javax.media.rtp.SessionListener;
import javax.media.rtp.event.ByeEvent;
import javax.media.rtp.event.NewParticipantEvent;
import javax.media.rtp.event.NewReceiveStreamEvent;
import javax.media.rtp.event.ReceiveStreamEvent;
import javax.media.rtp.event.RemotePayloadChangeEvent;
import javax.media.rtp.event.SessionEvent;
import javax.media.rtp.event.StreamMappedEvent;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
/**
* This class implements receive methods and listeners to be used in AudioChannel
*
* @author Thiago Camargo
*/
public class AudioReceiver implements ReceiveStreamListener, SessionListener,
ControllerListener {
private static final Logger LOGGER = Logger.getLogger(AudioReceiver.class.getName());
boolean dataReceived = false;
Object dataSync;
JingleMediaSession jingleMediaSession;
public AudioReceiver(final Object dataSync, final JingleMediaSession jingleMediaSession) {
this.dataSync = dataSync;
this.jingleMediaSession = jingleMediaSession;
}
/**
* JingleSessionListener.
*/
public synchronized void update(SessionEvent evt) {
if (evt instanceof NewParticipantEvent) {
Participant p = ((NewParticipantEvent) evt).getParticipant();
LOGGER.fine(" - 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) {
LOGGER.severe(" - Received an RTP PayloadChangeEvent.");
LOGGER.severe("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) {
LOGGER.severe(" - Recevied new RTP stream: " + ctl.getFormat());
}
else
LOGGER.severe(" - Recevied new RTP stream");
if (participant == null)
LOGGER.severe(" The sender of this stream had yet to be identified.");
else {
LOGGER.severe(" 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();
jingleMediaSession.mediaReceived(participant != null ? participant.getCNAME() : "");
// Notify intialize() that a new stream had arrived.
synchronized (dataSync) {
dataReceived = true;
dataSync.notifyAll();
}
}
catch (Exception e) {
LOGGER.severe("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");
LOGGER.severe(" - The previously unidentified stream ");
if (ctl != null)
LOGGER.severe(" " + ctl.getFormat());
LOGGER.severe(" had now been identified as sent by: " + participant.getCNAME());
}
}
else if (evt instanceof ByeEvent) {
LOGGER.severe(" - 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);
LOGGER.severe("Receiver internal error: " + ce);
}
}
}

View file

@ -0,0 +1,166 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.jmf;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.mediaimpl.JMFInit;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* 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 static final Logger LOGGER = Logger.getLogger(JmfMediaManager.class.getName());
public static final String MEDIA_NAME = "JMF";
private List<PayloadType> payloads = new ArrayList<PayloadType>();
private String mediaLocator = null;
/**
* Creates a Media Manager instance
*/
public JmfMediaManager(JingleTransportManager transportManager) {
super(transportManager);
setupPayloads();
}
/**
* Creates a Media Manager instance
*
* @param mediaLocator Media Locator
*/
public JmfMediaManager(String mediaLocator, JingleTransportManager transportManager) {
super(transportManager);
this.mediaLocator = mediaLocator;
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, final JingleSession jingleSession) {
return new AudioMediaSession(payloadType, remote, local, mediaLocator, jingleSession);
}
/**
* 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;
}
/**
* Return the media locator or null if not defined
*
* @return media locator
*/
public String getMediaLocator() {
return mediaLocator;
}
/**
* Set the media locator
*
* @param mediaLocator media locator or null to use default
*/
public void setMediaLocator(String mediaLocator) {
this.mediaLocator = mediaLocator;
}
/**
* 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) {
LOGGER.fine("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
}
public String getName() {
return MEDIA_NAME;
}
}

View file

@ -0,0 +1,241 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.jspeex;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.GeneralSecurityException;
import java.util.logging.Logger;
import javax.media.NoProcessorException;
import javax.media.format.UnsupportedFormatException;
import javax.media.rtp.rtcp.SenderReport;
import javax.media.rtp.rtcp.SourceDescription;
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.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* 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 static final Logger LOGGER = Logger.getLogger(AudioMediaSession.class.getName());
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.jspeex.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, JingleSession jingleSession) {
super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession);
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();
LOGGER.fine(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 {
LOGGER.fine("start");
mediaSession.start(true);
this.mediaReceived("");
}
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;
}
}

View file

@ -0,0 +1,131 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.jspeex;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.mediaimpl.JMFInit;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* 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 static final Logger LOGGER = Logger.getLogger(SpeexMediaManager.class.getName());
public static final String MEDIA_NAME = "Speex";
private List<PayloadType> payloads = new ArrayList<PayloadType>();
public SpeexMediaManager(JingleTransportManager transportManager) {
super(transportManager);
setupPayloads();
setupJMF();
}
/**
* Returns a new jingleMediaSession
*
* @param payloadType payloadType
* @param remote remote Candidate
* @param local local Candidate
* @return JingleMediaSession
*/
public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
return new AudioMediaSession(payloadType, remote, local, null,null);
}
/**
* 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) {
LOGGER.fine("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
}
public String getName() {
return MEDIA_NAME;
}
}

View file

@ -0,0 +1,102 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.multi;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
import java.util.ArrayList;
import java.util.List;
/**
* Implements a MultiMediaManager using other JingleMediaManager implementations.
* It supports every Codecs that JingleMediaManagers added has.
*
* @author Thiago Camargo
*/
public class MultiMediaManager extends JingleMediaManager {
public static final String MEDIA_NAME = "Multi";
private List<JingleMediaManager> managers = new ArrayList<JingleMediaManager>();
private PayloadType preferredPayloadType = null;
public MultiMediaManager(JingleTransportManager transportManager) {
super(transportManager);
}
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>();
if (preferredPayloadType != null) list.add(preferredPayloadType);
for (JingleMediaManager manager : managers) {
for (PayloadType payloadType : manager.getPayloads()) {
if (!list.contains(payloadType) && !payloadType.equals(preferredPayloadType))
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, final JingleSession jingleSession) {
for (JingleMediaManager manager : managers) {
if (manager.getPayloads().contains(payloadType)) {
return manager.createMediaSession(payloadType, remote, local, jingleSession);
}
}
return null;
}
public PayloadType getPreferredPayloadType() {
if (preferredPayloadType != null) return preferredPayloadType;
return super.getPreferredPayloadType();
}
public void setPreferredPayloadType(PayloadType preferredPayloadType) {
this.preferredPayloadType = preferredPayloadType;
}
public String getName() {
return MEDIA_NAME;
}
}

View file

@ -0,0 +1,111 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.sshare;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageDecoder;
import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageEncoder;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
import java.util.ArrayList;
import java.util.List;
/**
* Implements a JingleMediaManager for ScreenSharing.
* It currently uses an Audio payload Type. Which needs to be fixed in the next version.
*
* @author Thiago Camargo
*/
public class ScreenShareMediaManager extends JingleMediaManager {
public static final String MEDIA_NAME = "ScreenShare";
private List<PayloadType> payloads = new ArrayList<PayloadType>();
private ImageDecoder decoder = null;
private ImageEncoder encoder = null;
public ScreenShareMediaManager(JingleTransportManager transportManager) {
super(transportManager);
setupPayloads();
}
/**
* Setup API supported Payloads
*/
private void setupPayloads() {
payloads.add(new PayloadType.Audio(30, "sshare"));
}
/**
* Return all supported Payloads for this Manager.
*
* @return The Payload List
*/
public List<PayloadType> getPayloads() {
return payloads;
}
/**
* 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, final JingleSession jingleSession) {
ScreenShareSession session = null;
session = new ScreenShareSession(payloadType, remote, local, "Screen", jingleSession);
if (encoder != null) {
session.setEncoder(encoder);
}
if (decoder != null) {
session.setDecoder(decoder);
}
return session;
}
public PayloadType getPreferredPayloadType() {
return super.getPreferredPayloadType();
}
public ImageDecoder getDecoder() {
return decoder;
}
public void setDecoder(ImageDecoder decoder) {
this.decoder = decoder;
}
public ImageEncoder getEncoder() {
return encoder;
}
public void setEncoder(ImageEncoder encoder) {
this.encoder = encoder;
}
public String getName() {
return MEDIA_NAME;
}
}

View file

@ -0,0 +1,203 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.sshare;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageDecoder;
import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageEncoder;
import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageReceiver;
import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageTransmitter;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* This Class implements a complete JingleMediaSession.
* It sould be used to transmit and receive captured images from the Display.
* This Class should be automaticly controlled by JingleSession.
* 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 ScreenShareSession extends JingleMediaSession {
private static final Logger LOGGER = Logger.getLogger(ScreenShareSession.class.getName());
private ImageTransmitter transmitter = null;
private ImageReceiver receiver = null;
private int width = 600;
private int height = 600;
/**
* 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 ScreenShareSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local,
final String locator, JingleSession jingleSession) {
super(payloadType, remote, local, "Screen", jingleSession);
initialize();
}
/**
* Initialize the screen share channels.
*/
public void initialize() {
JingleSession session = getJingleSession();
if ((session != null) && (session.getInitiator().equals(session.getConnection().getUser()))) {
// If the initiator of the jingle session is us then we transmit a screen share.
try {
InetAddress remote = InetAddress.getByName(getRemote().getIp());
transmitter = new ImageTransmitter(new DatagramSocket(getLocal().getPort()), remote, getRemote().getPort(),
new Rectangle(0, 0, width, height));
} catch (Exception e) {
e.printStackTrace();
}
} else {
// Otherwise we receive a screen share.
JFrame window = new JFrame();
JPanel jp = new JPanel();
window.add(jp);
window.setLocation(0, 0);
window.setSize(600, 600);
window.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
receiver.stop();
}
});
try {
receiver = new ImageReceiver(InetAddress.getByName("0.0.0.0"), getRemote().getPort(), getLocal().getPort(), width,
height);
LOGGER.fine("Receiving on:" + receiver.getLocalPort());
} catch (UnknownHostException e) {
e.printStackTrace();
}
jp.add(receiver);
receiver.setVisible(true);
window.setAlwaysOnTop(true);
window.setVisible(true);
}
}
/**
* Starts transmission and for NAT Traversal reasons start receiving also.
*/
public void startTrasmit() {
new Thread(transmitter).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) {
transmitter.setTransmit(true);
}
/**
* 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 (transmitter != null) {
transmitter.stop();
}
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void stopReceive() {
if (receiver != null) {
receiver.stop();
}
}
/**
* 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;
}
public void setEncoder(ImageEncoder encoder) {
if (encoder != null) {
this.transmitter.setEncoder(encoder);
}
}
public void setDecoder(ImageDecoder decoder) {
if (decoder != null) {
this.receiver.setDecoder(decoder);
}
}
}

View file

@ -0,0 +1,99 @@
/**
*
* Copyright 2006 Jerry Huxtable
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorModel;
/**
* A convenience class which implements those methods of BufferedImageOp which are rarely changed.
*/
public abstract class AbstractBufferedImageOp implements BufferedImageOp, Cloneable {
public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
if ( dstCM == null )
dstCM = src.getColorModel();
return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dstCM.isAlphaPremultiplied(), null);
}
public Rectangle2D getBounds2D( BufferedImage src ) {
return new Rectangle(0, 0, src.getWidth(), src.getHeight());
}
public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) {
if ( dstPt == null )
dstPt = new Point2D.Double();
dstPt.setLocation( srcPt.getX(), srcPt.getY() );
return dstPt;
}
public RenderingHints getRenderingHints() {
return null;
}
/**
* A convenience method for getting ARGB pixels from an image. This tries to avoid the performance
* penalty of BufferedImage.getRGB unmanaging the image.
* @param image a BufferedImage object
* @param x the left edge of the pixel block
* @param y the right edge of the pixel block
* @param width the width of the pixel arry
* @param height the height of the pixel arry
* @param pixels the array to hold the returned pixels. May be null.
* @return the pixels
* @see #setRGB
*/
public int[] getRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
int type = image.getType();
if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
return (int [])image.getRaster().getDataElements( x, y, width, height, pixels );
return image.getRGB( x, y, width, height, pixels, 0, width );
}
/**
* A convenience method for setting ARGB pixels in an image. This tries to avoid the performance
* penalty of BufferedImage.setRGB unmanaging the image.
* @param image a BufferedImage object
* @param x the left edge of the pixel block
* @param y the right edge of the pixel block
* @param width the width of the pixel arry
* @param height the height of the pixel arry
* @param pixels the array of pixels to set
* @see #getRGB
*/
public void setRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
int type = image.getType();
if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
image.getRaster().setDataElements( x, y, width, height, pixels );
else
image.setRGB( x, y, width, height, pixels, 0, width );
}
public Object clone() {
try {
return super.clone();
}
catch ( CloneNotSupportedException e ) {
return null;
}
}
}

View file

@ -0,0 +1,32 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.mediaimpl.sshare.api;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* Implements a default PNG decoder.
*/
public class DefaultDecoder implements ImageDecoder {
public BufferedImage decode(ByteArrayInputStream stream) throws IOException {
return ImageIO.read(stream);
}
}

View file

@ -0,0 +1,40 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.mediaimpl.sshare.api;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* Implements a default PNG Encoder
*/
public class DefaultEncoder implements ImageEncoder{
public ByteArrayOutputStream encode(BufferedImage bufferedImage) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(bufferedImage, "png", baos);
}
catch (IOException e) {
e.printStackTrace();
baos = null;
}
return baos;
}
}

View file

@ -0,0 +1,31 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* Image Decoder Interface use this interface if you want to change the default decoder
*
* @author Thiago Rocha Camargo
*/
public interface ImageDecoder {
public BufferedImage decode(ByteArrayInputStream stream) throws IOException;
}

View file

@ -0,0 +1,29 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
/**
* Image Encoder Interface use this interface if you want to change the default encoder
*
* @author Thiago Rocha Camargo
*/
public interface ImageEncoder {
public ByteArrayOutputStream encode(BufferedImage bufferedImage);
}

View file

@ -0,0 +1,168 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* UDP Image Receiver.
* It uses PNG Tiles into UDP packets.
*
* @author Thiago Rocha Camargo
*/
public class ImageReceiver extends Canvas {
private static final long serialVersionUID = -7000112305305269025L;
private boolean on = true;
private DatagramSocket socket;
private BufferedImage[][] tiles;
private static final int tileWidth = ImageTransmitter.tileWidth;
private InetAddress localHost;
private InetAddress remoteHost;
private int localPort;
private int remotePort;
private ImageDecoder decoder;
public ImageReceiver(final InetAddress remoteHost, final int remotePort, final int localPort, int width, int height) {
tiles = new BufferedImage[width][height];
try {
socket = new DatagramSocket(localPort);
localHost = socket.getLocalAddress();
this.remoteHost = remoteHost;
this.remotePort = remotePort;
this.localPort = localPort;
this.decoder = new DefaultDecoder();
new Thread(new Runnable() {
public void run() {
byte[] buf = new byte[1024];
DatagramPacket p = new DatagramPacket(buf, 1024);
try {
while (on) {
socket.receive(p);
int length = p.getLength();
BufferedImage bufferedImage = decoder.decode(new ByteArrayInputStream(p.getData(), 0, length - 2));
if (bufferedImage != null) {
int x = p.getData()[length - 2];
int y = p.getData()[length - 1];
drawTile(x, y, bufferedImage);
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
byte[] buf = new byte[1024];
DatagramPacket p = new DatagramPacket(buf, 1024);
try {
while (on) {
p.setAddress(remoteHost);
p.setPort(remotePort);
socket.send(p);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
catch (SocketException e) {
e.printStackTrace();
}
this.setSize(width, height);
}
public InetAddress getLocalHost() {
return localHost;
}
public InetAddress getRemoteHost() {
return remoteHost;
}
public int getLocalPort() {
return localPort;
}
public int getRemotePort() {
return remotePort;
}
public DatagramSocket getDatagramSocket() {
return socket;
}
public void drawTile(int x, int y, BufferedImage bufferedImage) {
tiles[x][y] = bufferedImage;
//repaint(x * tileWidth, y * tileWidth, tileWidth, tileWidth);
this.getGraphics().drawImage(bufferedImage, tileWidth * x, tileWidth * y, this);
}
public void paint(Graphics g) {
for (int i = 0; i < tiles.length; i++) {
for (int j = 0; j < tiles[0].length; j++) {
g.drawImage(tiles[i][j], tileWidth * i, tileWidth * j, this);
}
}
}
public ImageDecoder getDecoder() {
return decoder;
}
public void setDecoder(ImageDecoder decoder) {
this.decoder = decoder;
}
public void stop(){
this.on=false;
socket.close();
}
}

View file

@ -0,0 +1,219 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.logging.Logger;
/**
* UDP Image Receiver.
* It uses PNG Tiles into UDP packets.
*
* @author Thiago Rocha Camargo
*/
public class ImageTransmitter implements Runnable {
private static final Logger LOGGER = Logger.getLogger(ImageTransmitter.class.getName());
private Robot robot;
private InetAddress localHost;
private InetAddress remoteHost;
private int localPort;
private int remotePort;
public static final int tileWidth = 25;
private boolean on = true;
private boolean transmit = false;
private DatagramSocket socket;
private Rectangle area;
private int[][][] tiles;
private int maxI;
private int maxJ;
private ImageEncoder encoder;
public final static int KEYFRAME = 10;
public ImageTransmitter(DatagramSocket socket, InetAddress remoteHost, int remotePort, Rectangle area) {
try {
robot = new Robot();
maxI = (int) Math.ceil(area.getWidth() / tileWidth);
maxJ = (int) Math.ceil(area.getHeight() / tileWidth);
tiles = new int[maxI][maxJ][tileWidth * tileWidth];
this.area = area;
this.socket = socket;
localHost = socket.getLocalAddress();
localPort = socket.getLocalPort();
this.remoteHost = remoteHost;
this.remotePort = remotePort;
this.encoder = new DefaultEncoder();
transmit = true;
}
catch (AWTException e) {
e.printStackTrace();
}
}
public void start() {
byte[] buf = new byte[1024];
final DatagramPacket p = new DatagramPacket(buf, 1024);
int keyframe = 0;
while (on) {
if (transmit) {
BufferedImage capture = robot.createScreenCapture(area);
QuantizeFilter filter = new QuantizeFilter();
capture = filter.filter(capture, null);
long trace = System.currentTimeMillis();
if (++keyframe > KEYFRAME) {
keyframe = 0;
}
LOGGER.fine("KEYFRAME:" + keyframe);
for (int i = 0; i < maxI; i++) {
for (int j = 0; j < maxJ; j++) {
final BufferedImage bufferedImage = capture.getSubimage(i * tileWidth, j * tileWidth, tileWidth, tileWidth);
int[] pixels = new int[tileWidth * tileWidth];
PixelGrabber pg = new PixelGrabber(bufferedImage, 0, 0, tileWidth, tileWidth, pixels, 0, tileWidth);
try {
if (pg.grabPixels()) {
if (keyframe == KEYFRAME || !Arrays.equals(tiles[i][j], pixels)) {
ByteArrayOutputStream baos = encoder.encode(bufferedImage);
if (baos != null) {
try {
Thread.sleep(1);
baos.write(i);
baos.write(j);
byte[] bytesOut = baos.toByteArray();
if (bytesOut.length > 1000)
LOGGER.severe("Bytes out > 1000. Equals " + bytesOut.length);
p.setData(bytesOut);
p.setAddress(remoteHost);
p.setPort(remotePort);
try {
socket.send(p);
}
catch (IOException e) {
e.printStackTrace();
}
tiles[i][j] = pixels;
}
catch (Exception e) {
}
}
}
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
trace = (System.currentTimeMillis() - trace);
LOGGER.fine("Loop Time:" + trace);
if (trace < 500) {
try {
Thread.sleep(500 - trace);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public void run() {
start();
}
/**
* Set Transmit Enabled/Disabled
*
* @param transmit boolean Enabled/Disabled
*/
public void setTransmit(boolean transmit) {
this.transmit = transmit;
}
/**
* Get the encoder used to encode Images Tiles
*
* @return encoder
*/
public ImageEncoder getEncoder() {
return encoder;
}
/**
* Set the encoder used to encode Image Tiles
*
* @param encoder encoder
*/
public void setEncoder(ImageEncoder encoder) {
this.encoder = encoder;
}
/**
* Stops Transmitter
*/
public void stop() {
this.transmit = false;
this.on = false;
socket.close();
}
}

View file

@ -0,0 +1,286 @@
/**
*
* Copyright 2006 Jerry Huxtable
*
* 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.jingleold.mediaimpl.sshare.api;
import java.io.PrintStream;
import java.util.Vector;
import java.util.logging.Logger;
/**
* An image Quantizer based on the Octree algorithm. This is a very basic implementation
* at present and could be much improved by picking the nodes to reduce more carefully
* (i.e. not completely at random) when I get the time.
*/
public class OctTreeQuantizer implements Quantizer {
private static final Logger LOGGER = Logger.getLogger(OctTreeQuantizer.class.getName());
/**
* The greatest depth the tree is allowed to reach
*/
final static int MAX_LEVEL = 5;
/**
* An Octtree node.
*/
class OctTreeNode {
int children;
int level;
OctTreeNode parent;
OctTreeNode[] leaf = new OctTreeNode[8];
boolean isLeaf;
int count;
int totalRed;
int totalGreen;
int totalBlue;
int index;
/**
* A debugging method which prints the tree out.
*/
public void list(PrintStream s, int level) {
String indentStr = "";
for (int i = 0; i < level; i++)
indentStr += " ";
if (count == 0)
LOGGER.fine(indentStr + index + ": count=" + count);
else
LOGGER.fine(indentStr + index + ": count=" + count + " red=" + (totalRed/count) + " green=" + (totalGreen / count) + " blue=" + (totalBlue / count));
for (int i = 0; i < 8; i++)
if (leaf[i] != null)
leaf[i].list(s, level+2);
}
}
private int nodes = 0;
private OctTreeNode root;
private int reduceColors;
private int maximumColors;
private int colors = 0;
private Vector<OctTreeNode>[] colorList;
public OctTreeQuantizer() {
setup(256);
colorList = new Vector[MAX_LEVEL+1];
for (int i = 0; i < MAX_LEVEL+1; i++)
colorList[i] = new Vector<OctTreeNode>();
root = new OctTreeNode();
}
/**
* Initialize the quantizer. This should be called before adding any pixels.
* @param numColors the number of colors we're quantizing to.
*/
public void setup(int numColors) {
maximumColors = numColors;
reduceColors = Math.max(512, numColors * 2);
}
/**
* Add pixels to the quantizer.
* @param pixels the array of ARGB pixels
* @param offset the offset into the array
* @param count the count of pixels
*/
public void addPixels(int[] pixels, int offset, int count) {
for (int i = 0; i < count; i++) {
insertColor(pixels[i+offset]);
if (colors > reduceColors)
reduceTree(reduceColors);
}
}
/**
* Get the color table index for a color.
* @param rgb the color
* @return the index
*/
public int getIndexForColor(int rgb) {
int red = (rgb >> 16) & 0xff;
int green = (rgb >> 8) & 0xff;
int blue = rgb & 0xff;
OctTreeNode node = root;
for (int level = 0; level <= MAX_LEVEL; level++) {
OctTreeNode child;
int bit = 0x80 >> level;
int index = 0;
if ((red & bit) != 0)
index += 4;
if ((green & bit) != 0)
index += 2;
if ((blue & bit) != 0)
index += 1;
child = node.leaf[index];
if (child == null)
return node.index;
else if (child.isLeaf)
return child.index;
else
node = child;
}
LOGGER.fine("getIndexForColor failed");
return 0;
}
private void insertColor(int rgb) {
int red = (rgb >> 16) & 0xff;
int green = (rgb >> 8) & 0xff;
int blue = rgb & 0xff;
OctTreeNode node = root;
// LOGGER.fine("insertColor="+Integer.toHexString(rgb));
for (int level = 0; level <= MAX_LEVEL; level++) {
OctTreeNode child;
int bit = 0x80 >> level;
int index = 0;
if ((red & bit) != 0)
index += 4;
if ((green & bit) != 0)
index += 2;
if ((blue & bit) != 0)
index += 1;
child = node.leaf[index];
if (child == null) {
node.children++;
child = new OctTreeNode();
child.parent = node;
node.leaf[index] = child;
node.isLeaf = false;
nodes++;
colorList[level].addElement(child);
if (level == MAX_LEVEL) {
child.isLeaf = true;
child.count = 1;
child.totalRed = red;
child.totalGreen = green;
child.totalBlue = blue;
child.level = level;
colors++;
return;
}
node = child;
} else if (child.isLeaf) {
child.count++;
child.totalRed += red;
child.totalGreen += green;
child.totalBlue += blue;
return;
} else
node = child;
}
LOGGER.fine("insertColor failed");
}
private void reduceTree(int numColors) {
for (int level = MAX_LEVEL-1; level >= 0; level--) {
Vector<OctTreeNode> v = colorList[level];
if (v != null && v.size() > 0) {
for (int j = 0; j < v.size(); j++) {
OctTreeNode node = v.elementAt(j);
if (node.children > 0) {
for (int i = 0; i < 8; i++) {
OctTreeNode child = node.leaf[i];
if (child != null) {
if (!child.isLeaf)
LOGGER.fine("not a leaf!");
node.count += child.count;
node.totalRed += child.totalRed;
node.totalGreen += child.totalGreen;
node.totalBlue += child.totalBlue;
node.leaf[i] = null;
node.children--;
colors--;
nodes--;
colorList[level+1].removeElement(child);
}
}
node.isLeaf = true;
colors++;
if (colors <= numColors)
return;
}
}
}
}
LOGGER.fine("Unable to reduce the OctTree");
}
/**
* Build the color table.
* @return the color table
*/
public int[] buildColorTable() {
int[] table = new int[colors];
buildColorTable(root, table, 0);
return table;
}
/**
* A quick way to use the quantizer. Just create a table the right size and pass in the pixels.
* @param inPixels the input colors
* @param table the output color table
*/
public void buildColorTable(int[] inPixels, int[] table) {
int count = inPixels.length;
maximumColors = table.length;
for (int i = 0; i < count; i++) {
insertColor(inPixels[i]);
if (colors > reduceColors)
reduceTree(reduceColors);
}
if (colors > maximumColors)
reduceTree(maximumColors);
buildColorTable(root, table, 0);
}
private int buildColorTable(OctTreeNode node, int[] table, int index) {
if (colors > maximumColors)
reduceTree(maximumColors);
if (node.isLeaf) {
int count = node.count;
table[index] = 0xff000000 |
((node.totalRed/count) << 16) |
((node.totalGreen/count) << 8) |
node.totalBlue/count;
node.index = index++;
} else {
for (int i = 0; i < 8; i++) {
if (node.leaf[i] != null) {
node.index = index;
index = buildColorTable(node.leaf[i], table, index);
}
}
}
return index;
}
}

View file

@ -0,0 +1,224 @@
/**
*
* Copyright 2006 Jerry Huxtable
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.Color;
import java.util.Random;
/**
* Some more useful math functions for image processing.
* These are becoming obsolete as we move to Java2D. Use MiscComposite instead.
*/
public class PixelUtils {
public final static int REPLACE = 0;
public final static int NORMAL = 1;
public final static int MIN = 2;
public final static int MAX = 3;
public final static int ADD = 4;
public final static int SUBTRACT = 5;
public final static int DIFFERENCE = 6;
public final static int MULTIPLY = 7;
public final static int HUE = 8;
public final static int SATURATION = 9;
public final static int VALUE = 10;
public final static int COLOR = 11;
public final static int SCREEN = 12;
public final static int AVERAGE = 13;
public final static int OVERLAY = 14;
public final static int CLEAR = 15;
public final static int EXCHANGE = 16;
public final static int DISSOLVE = 17;
public final static int DST_IN = 18;
public final static int ALPHA = 19;
public final static int ALPHA_TO_GRAY = 20;
private static Random randomGenerator = new Random();
/**
* Clamp a value to the range 0..255
*/
public static int clamp(int c) {
if (c < 0)
return 0;
if (c > 255)
return 255;
return c;
}
public static int interpolate(int v1, int v2, float f) {
return clamp((int)(v1+f*(v2-v1)));
}
public static int brightness(int rgb) {
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
return (r+g+b)/3;
}
public static boolean nearColors(int rgb1, int rgb2, int tolerance) {
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance;
}
private final static float[] hsb1 = new float[3];//FIXME-not thread safe
private final static float[] hsb2 = new float[3];//FIXME-not thread safe
// Return rgb1 painted onto rgb2
public static int combinePixels(int rgb1, int rgb2, int op) {
return combinePixels(rgb1, rgb2, op, 0xff);
}
public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha, int channelMask) {
return (rgb2 & ~channelMask) | combinePixels(rgb1 & channelMask, rgb2, op, extraAlpha);
}
public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha) {
if (op == REPLACE)
return rgb1;
int a1 = (rgb1 >> 24) & 0xff;
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int a2 = (rgb2 >> 24) & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
switch (op) {
case NORMAL:
break;
case MIN:
r1 = Math.min(r1, r2);
g1 = Math.min(g1, g2);
b1 = Math.min(b1, b2);
break;
case MAX:
r1 = Math.max(r1, r2);
g1 = Math.max(g1, g2);
b1 = Math.max(b1, b2);
break;
case ADD:
r1 = clamp(r1+r2);
g1 = clamp(g1+g2);
b1 = clamp(b1+b2);
break;
case SUBTRACT:
r1 = clamp(r2-r1);
g1 = clamp(g2-g1);
b1 = clamp(b2-b1);
break;
case DIFFERENCE:
r1 = clamp(Math.abs(r1-r2));
g1 = clamp(Math.abs(g1-g2));
b1 = clamp(Math.abs(b1-b2));
break;
case MULTIPLY:
r1 = clamp(r1*r2/255);
g1 = clamp(g1*g2/255);
b1 = clamp(b1*b2/255);
break;
case DISSOLVE:
if ((randomGenerator.nextInt() & 0xff) <= a1) {
r1 = r2;
g1 = g2;
b1 = b2;
}
break;
case AVERAGE:
r1 = (r1+r2)/2;
g1 = (g1+g2)/2;
b1 = (b1+b2)/2;
break;
case HUE:
case SATURATION:
case VALUE:
case COLOR:
Color.RGBtoHSB(r1, g1, b1, hsb1);
Color.RGBtoHSB(r2, g2, b2, hsb2);
switch (op) {
case HUE:
hsb2[0] = hsb1[0];
break;
case SATURATION:
hsb2[1] = hsb1[1];
break;
case VALUE:
hsb2[2] = hsb1[2];
break;
case COLOR:
hsb2[0] = hsb1[0];
hsb2[1] = hsb1[1];
break;
}
rgb1 = Color.HSBtoRGB(hsb2[0], hsb2[1], hsb2[2]);
r1 = (rgb1 >> 16) & 0xff;
g1 = (rgb1 >> 8) & 0xff;
b1 = rgb1 & 0xff;
break;
case SCREEN:
r1 = 255 - ((255 - r1) * (255 - r2)) / 255;
g1 = 255 - ((255 - g1) * (255 - g2)) / 255;
b1 = 255 - ((255 - b1) * (255 - b2)) / 255;
break;
case OVERLAY:
int m, s;
s = 255 - ((255 - r1) * (255 - r2)) / 255;
m = r1 * r2 / 255;
r1 = (s * r1 + m * (255 - r1)) / 255;
s = 255 - ((255 - g1) * (255 - g2)) / 255;
m = g1 * g2 / 255;
g1 = (s * g1 + m * (255 - g1)) / 255;
s = 255 - ((255 - b1) * (255 - b2)) / 255;
m = b1 * b2 / 255;
b1 = (s * b1 + m * (255 - b1)) / 255;
break;
case CLEAR:
r1 = g1 = b1 = 0xff;
break;
case DST_IN:
r1 = clamp((r2*a1)/255);
g1 = clamp((g2*a1)/255);
b1 = clamp((b2*a1)/255);
a1 = clamp((a2*a1)/255);
return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
case ALPHA:
a1 = a1*a2/255;
return (a1 << 24) | (r2 << 16) | (g2 << 8) | b2;
case ALPHA_TO_GRAY:
int na = 255-a1;
return (a1 << 24) | (na << 16) | (na << 8) | na;
}
if (extraAlpha != 0xff || a1 != 0xff) {
a1 = a1*extraAlpha/255;
int a3 = (255-a1)*a2/255;
r1 = clamp((r1*a1+r2*a3)/255);
g1 = clamp((g1*a1+g2*a3)/255);
b1 = clamp((b1*a1+b2*a3)/255);
a1 = clamp(a1+a3);
}
return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
}
}

View file

@ -0,0 +1,178 @@
/**
*
* Copyright 2006 Jerry Huxtable
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.Rectangle;
/**
* A filter which quantizes an image to a set number of colors - useful for producing
* images which are to be encoded using an index color model. The filter can perform
* Floyd-Steinberg error-diffusion dithering if required. At present, the quantization
* is done using an octtree algorithm but I eventually hope to add more quantization
* methods such as median cut. Note: at present, the filter produces an image which
* uses the RGB color model (because the application it was written for required it).
* I hope to extend it to produce an IndexColorModel by request.
*/
public class QuantizeFilter extends WholeImageFilter {
/**
* Floyd-Steinberg dithering matrix.
*/
protected final static int[] matrix = {
0, 0, 0,
0, 0, 7,
3, 5, 1,
};
private int sum = 3+5+7+1;
private boolean dither;
private int numColors = 256;
private boolean serpentine = true;
/**
* Set the number of colors to quantize to.
* @param numColors the number of colors. The default is 256.
*/
public void setNumColors(int numColors) {
this.numColors = Math.min(Math.max(numColors, 8), 256);
}
/**
* Get the number of colors to quantize to.
* @return the number of colors.
*/
public int getNumColors() {
return numColors;
}
/**
* Set whether to use dithering or not. If not, the image is posterized.
* @param dither true to use dithering
*/
public void setDither(boolean dither) {
this.dither = dither;
}
/**
* Return the dithering setting
* @return the current setting
*/
public boolean getDither() {
return dither;
}
/**
* Set whether to use a serpentine pattern for return or not. This can reduce 'avalanche' artifacts in the output.
* @param serpentine true to use serpentine pattern
*/
public void setSerpentine(boolean serpentine) {
this.serpentine = serpentine;
}
/**
* Return the serpentine setting
* @return the current setting
*/
public boolean getSerpentine() {
return serpentine;
}
public void quantize(int[] inPixels, int[] outPixels, int width, int height, int numColors, boolean dither, boolean serpentine) {
int count = width*height;
Quantizer quantizer = new OctTreeQuantizer();
quantizer.setup(numColors);
quantizer.addPixels(inPixels, 0, count);
int[] table = quantizer.buildColorTable();
if (!dither) {
for (int i = 0; i < count; i++)
outPixels[i] = table[quantizer.getIndexForColor(inPixels[i])];
} else {
int index = 0;
for (int y = 0; y < height; y++) {
boolean reverse = serpentine && (y & 1) == 1;
int direction;
if (reverse) {
index = y*width+width-1;
direction = -1;
} else {
index = y*width;
direction = 1;
}
for (int x = 0; x < width; x++) {
int rgb1 = inPixels[index];
int rgb2 = table[quantizer.getIndexForColor(rgb1)];
outPixels[index] = rgb2;
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
int er = r1-r2;
int eg = g1-g2;
int eb = b1-b2;
for (int i = -1; i <= 1; i++) {
int iy = i+y;
if (0 <= iy && iy < height) {
for (int j = -1; j <= 1; j++) {
int jx = j+x;
if (0 <= jx && jx < width) {
int w;
if (reverse)
w = matrix[(i+1)*3-j+1];
else
w = matrix[(i+1)*3+j+1];
if (w != 0) {
int k = reverse ? index - j : index + j;
rgb1 = inPixels[k];
r1 = (rgb1 >> 16) & 0xff;
g1 = (rgb1 >> 8) & 0xff;
b1 = rgb1 & 0xff;
r1 += er * w/sum;
g1 += eg * w/sum;
b1 += eb * w/sum;
inPixels[k] = (PixelUtils.clamp(r1) << 16) | (PixelUtils.clamp(g1) << 8) | PixelUtils.clamp(b1);
}
}
}
}
}
index += direction;
}
}
}
}
protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
int[] outPixels = new int[width*height];
quantize(inPixels, outPixels, width, height, numColors, dither, serpentine);
return outPixels;
}
public String toString() {
return "Colors/Quantize...";
}
}

View file

@ -0,0 +1,53 @@
/**
*
* Copyright 2006 Jerry Huxtable
*
* 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.jingleold.mediaimpl.sshare.api;
/**
* The interface for an image quantizer. The addColor method is called (repeatedly
* if necessary) with all the image pixels. A color table can then be returned by
* calling the buildColorTable method.
*/
public interface Quantizer {
/**
* Initialize the quantizer. This should be called before adding any pixels.
* @param numColors the number of colors we're quantizing to.
*/
public void setup(int numColors);
/**
* Add pixels to the quantizer.
* @param pixels the array of ARGB pixels
* @param offset the offset into the array
* @param count the count of pixels
*/
public void addPixels(int[] pixels, int offset, int count);
/**
* Build a color table from the added pixels.
* @return an array of ARGB pixels representing a color table
*/
public int[] buildColorTable();
/**
* Using the previously-built color table, return the index into that table for a pixel.
* This is guaranteed to return a valid index - returning the index of a color closer
* to that requested if necessary.
* @param rgb the pixel to find
* @return the pixel's index in the color table
*/
public int getIndexForColor(int rgb);
}

View file

@ -0,0 +1,86 @@
/**
*
* Copyright 2006 Jerry Huxtable
*
* 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.jingleold.mediaimpl.sshare.api;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
/**
* A filter which acts as a superclass for filters which need to have the whole image in memory
* to do their stuff.
*/
public abstract class WholeImageFilter extends AbstractBufferedImageOp {
/**
* The output image bounds.
*/
protected Rectangle transformedSpace;
/**
* The input image bounds.
*/
protected Rectangle originalSpace;
/**
* Construct a WholeImageFilter.
*/
public WholeImageFilter() {
}
public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
int width = src.getWidth();
int height = src.getHeight();
int type = src.getType();
WritableRaster srcRaster = src.getRaster();
originalSpace = new Rectangle(0, 0, width, height);
transformedSpace = new Rectangle(0, 0, width, height);
transformSpace(transformedSpace);
if ( dst == null ) {
ColorModel dstCM = src.getColorModel();
dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(transformedSpace.width, transformedSpace.height), dstCM.isAlphaPremultiplied(), null);
}
WritableRaster dstRaster = dst.getRaster();
int[] inPixels = getRGB( src, 0, 0, width, height, null );
inPixels = filterPixels( width, height, inPixels, transformedSpace );
setRGB( dst, 0, 0, transformedSpace.width, transformedSpace.height, inPixels );
return dst;
}
/**
* Calculate output bounds for given input bounds.
* @param rect input and output rectangle
*/
protected void transformSpace(Rectangle rect) {
}
/**
* Actually filter the pixels.
* @param width the image width
* @param height the image height
* @param inPixels the image pixels
* @param transformedSpace the output bounds
* @return the output pixels
*/
protected abstract int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace );
}

View file

@ -0,0 +1,90 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.test;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* Implements a MediaManager for test purposes.
*
* @author Thiago Camargo
*/
public class TestMediaManager extends JingleMediaManager {
public static final String MEDIA_NAME = "TestMedia";
private List<PayloadType> payloads = new ArrayList<PayloadType>();
private PayloadType preferredPayloadType = null;
public TestMediaManager(JingleTransportManager transportManager) {
super(transportManager);
}
/**
* Return all supported Payloads for this Manager.
*
* @return The Payload List
*/
public List<PayloadType> getPayloads() {
return payloads;
}
public void setPayloads(List<PayloadType> payloads) {
this.payloads.addAll(payloads);
}
/**
* 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, final JingleSession jingleSession) {
TestMediaSession session = null;
session = new TestMediaSession(payloadType, remote, local, "", jingleSession);
return session;
}
public PayloadType getPreferredPayloadType() {
if (preferredPayloadType != null)
return preferredPayloadType;
return super.getPreferredPayloadType();
}
public void setPreferredPayloadType(PayloadType preferredPayloadType) {
this.preferredPayloadType = preferredPayloadType;
}
public String getName() {
return MEDIA_NAME;
}
}

View file

@ -0,0 +1,89 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.mediaimpl.test;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
/**
* This Class implements a complete JingleMediaSession for unit testing.
*
* @author Thiago Camargo
*/
public class TestMediaSession extends JingleMediaSession {
/**
* Creates a TestMediaSession 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 TestMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local,
final String locator, JingleSession jingleSession) {
super(payloadType, remote, local, "Test", jingleSession);
initialize();
}
/**
* Initialize the screen share channels.
*/
public void initialize() {
}
/**
* Starts transmission and for NAT Traversal reasons start receiving also.
*/
public void startTrasmit() {
}
/**
* 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 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() {
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void stopReceive() {
}
}

View file

@ -0,0 +1,121 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold.nat;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.JingleSession;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* Basic Resolver takes all IP addresses of the interfaces and uses the
* first non-loopback address.
* A very simple and easy to use resolver.
*/
public class BasicResolver extends TransportResolver {
/**
* Constructor.
*/
public BasicResolver() {
super();
}
/**
* Resolve the IP address.
* <p/>
* The BasicResolver takes the IP addresses of the interfaces and uses the
* first non-loopback, non-linklocal and non-sitelocal address.
* @throws NotConnectedException
*/
public synchronized void resolve(JingleSession session) throws XMPPException, NotConnectedException {
setResolveInit();
clearCandidates();
Enumeration<NetworkInterface> ifaces = null;
try {
ifaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
e.printStackTrace();
}
while (ifaces.hasMoreElements()) {
NetworkInterface iface = ifaces.nextElement();
Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = iaddresses.nextElement();
if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !iaddress.isSiteLocalAddress()) {
TransportCandidate tr = new TransportCandidate.Fixed(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName(), getFreePort());
tr.setLocalIp(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName());
addCandidate(tr);
setResolveEnd();
return;
}
}
}
try {
ifaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
e.printStackTrace();
}
while (ifaces.hasMoreElements()) {
NetworkInterface iface = ifaces.nextElement();
Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = iaddresses.nextElement();
if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress()) {
TransportCandidate tr = new TransportCandidate.Fixed(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName(), getFreePort());
tr.setLocalIp(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName());
addCandidate(tr);
setResolveEnd();
return;
}
}
}
try {
TransportCandidate tr = new TransportCandidate.Fixed(InetAddress.getLocalHost().getHostAddress() != null ? InetAddress.getLocalHost().getHostAddress() : InetAddress.getLocalHost().getHostName(), getFreePort());
tr.setLocalIp(InetAddress.getLocalHost().getHostAddress() != null ? InetAddress.getLocalHost().getHostAddress() : InetAddress.getLocalHost().getHostName());
addCandidate(tr);
} catch (Exception e) {
e.printStackTrace();
}
setResolveEnd();
}
public void initialize() throws XMPPException {
setInitialized();
}
public void cancel() throws XMPPException {
// Nothing to do here
}
}

View file

@ -0,0 +1,30 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.nat;
import org.jivesoftware.smackx.jingleold.JingleSession;
/**
* A Basic Jingle Transport Manager implementation.
*
*/
public class BasicTransportManager extends JingleTransportManager{
protected TransportResolver createResolver(JingleSession session) {
return new BasicResolver();
}
}

View file

@ -0,0 +1,151 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold.nat;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smackx.jingleold.JingleSession;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Random;
/**
* Bridged Resolver use a RTPBridge Service to add a relayed candidate.
* A very reliable solution for NAT Traversal.
* <p/>
* The resolver verify is the XMPP Server that the client is connected offer this service.
* If the server supports, a candidate is requested from the service.
* The resolver adds this candidate
*/
public class BridgedResolver extends TransportResolver {
XMPPConnection connection;
Random random = new Random();
long sid;
/**
* Constructor.
* A Bridged Resolver need a XMPPConnection to connect to a RTP Bridge.
*/
public BridgedResolver(XMPPConnection connection) {
super();
this.connection = connection;
}
/**
* Resolve Bridged Candidate.
* <p/>
* The BridgedResolver takes the IP addresse and ports of a jmf proxy service.
* @throws NotConnectedException
*/
public synchronized void resolve(JingleSession session) throws XMPPException, NotConnectedException {
setResolveInit();
clearCandidates();
sid = Math.abs(random.nextLong());
RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid));
String localIp = getLocalHost();
TransportCandidate localCandidate = new TransportCandidate.Fixed(
rtpBridge.getIp(), rtpBridge.getPortA());
localCandidate.setLocalIp(localIp);
TransportCandidate remoteCandidate = new TransportCandidate.Fixed(
rtpBridge.getIp(), rtpBridge.getPortB());
remoteCandidate.setLocalIp(localIp);
localCandidate.setSymmetric(remoteCandidate);
remoteCandidate.setSymmetric(localCandidate);
localCandidate.setPassword(rtpBridge.getPass());
remoteCandidate.setPassword(rtpBridge.getPass());
localCandidate.setSessionId(rtpBridge.getSid());
remoteCandidate.setSessionId(rtpBridge.getSid());
localCandidate.setConnection(this.connection);
remoteCandidate.setConnection(this.connection);
addCandidate(localCandidate);
setResolveEnd();
}
public void initialize() throws SmackException, XMPPErrorException {
clearCandidates();
if (!RTPBridge.serviceAvailable(connection)) {
setInitialized();
throw new SmackException("No RTP Bridge service available");
}
setInitialized();
}
public void cancel() throws XMPPException {
// Nothing to do here
}
public static String getLocalHost() {
Enumeration<NetworkInterface> ifaces = null;
try {
ifaces = NetworkInterface.getNetworkInterfaces();
}
catch (SocketException e) {
e.printStackTrace();
}
while (ifaces.hasMoreElements()) {
NetworkInterface iface = ifaces.nextElement();
Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = iaddresses.nextElement();
if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !iaddress.isSiteLocalAddress() && !(iaddress instanceof Inet6Address)) {
return iaddress.getHostAddress();
}
}
}
try {
return InetAddress.getLocalHost().getHostAddress();
}
catch (Exception e) {
e.printStackTrace();
}
return "127.0.0.1";
}
}

View file

@ -0,0 +1,81 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.nat;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.listeners.CreatedJingleSessionListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
/**
* A Jingle Transport Manager implementation to be used for NAT Networks.
* This kind of transport needs that the connected XMPP Server provide a Bridge Service. (http://www.jivesoftware.com/protocol/rtpbridge)
* To relay the jmf outside the NAT.
*
* @author Thiago Camargo
*/
public class BridgedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener {
XMPPConnection xmppConnection;
public BridgedTransportManager(XMPPConnection xmppConnection) {
super();
this.xmppConnection = xmppConnection;
}
/**
* Return the correspondent resolver
*
* @param session correspondent Jingle Session
* @return resolver
*/
protected TransportResolver createResolver(JingleSession session) {
BridgedResolver bridgedResolver = new BridgedResolver(this.xmppConnection);
return bridgedResolver;
}
// Implement a Session Listener to relay candidates after establishment
public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) throws NotConnectedException {
RTPBridge rtpBridge = RTPBridge.relaySession(lc.getConnection(), lc.getSessionId(), lc.getPassword(), rc, lc);
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
}
public void sessionClosed(String reason, JingleSession jingleSession) {
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
}
public void sessionMediaReceived(JingleSession jingleSession, String participant) {
// Do Nothing
}
// Session Created
public void sessionCreated(JingleSession jingleSession) {
jingleSession.addListener(this);
}
}

View file

@ -0,0 +1,37 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.nat;
import java.net.DatagramPacket;
/**
* Listener for datagram packets received.
*
* @author Thiago Camargo
*/
public interface DatagramListener {
/**
* Called when a datagram is received. If the method returns false, the
* packet MUST NOT be resent from the received Channel.
*
* @param datagramPacket the datagram packet received.
* @return ?
*/
public boolean datagramReceived(DatagramPacket datagramPacket);
}

View file

@ -0,0 +1,85 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.nat;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.JingleSession;
/**
* The FixedResolver is a resolver where
* the external address and port are previously known when the object is
* initialized.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public class FixedResolver extends TransportResolver {
TransportCandidate fixedCandidate;
/**
* Constructor.
*/
public FixedResolver(String ip, int port) {
super();
setFixedCandidate(ip, port);
}
/**
* Create a basic resolver, where we provide the IP and port.
*
* @param ip an IP address
* @param port a port
*/
public void setFixedCandidate(String ip, int port) {
fixedCandidate = new TransportCandidate.Fixed(ip, port);
}
/**
* Resolve the IP address.
* @throws NotConnectedException
*/
public synchronized void resolve(JingleSession session) throws XMPPException, NotConnectedException {
if (!isResolving()) {
setResolveInit();
clearCandidates();
if (fixedCandidate.getLocalIp() == null)
fixedCandidate.setLocalIp(fixedCandidate.getIp());
if (fixedCandidate != null) {
addCandidate(fixedCandidate);
}
setResolveEnd();
}
}
/**
* Initialize the resolver.
*
* @throws XMPPException
*/
public void initialize() throws XMPPException {
setInitialized();
}
public void cancel() throws XMPPException {
// Nothing to do here
}
}

View file

@ -0,0 +1,63 @@
/**
*
* Copyright 2003-2006 Jive Software.
*
* 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.jingleold.nat;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.JingleSession;
import org.jivesoftware.smackx.jingleold.listeners.CreatedJingleSessionListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
/**
* A Fixed Jingle Transport Manager implementation.
*
*/
public class FixedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener {
FixedResolver resolver;
public FixedTransportManager(FixedResolver inResolver) {
resolver = inResolver;
}
protected TransportResolver createResolver(JingleSession session) {
return resolver;
}
public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) {
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
}
public void sessionClosed(String reason, JingleSession jingleSession) {
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
}
public void sessionMediaReceived(JingleSession jingleSession, String participant) {
// Do Nothing
}
public void sessionCreated(JingleSession jingleSession) {
jingleSession.addListener(this);
}
}

View file

@ -0,0 +1,146 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.nat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A very Simple HTTP Server
*/
public class HttpServer {
private static final Logger LOGGER = Logger.getLogger(HttpServer.class.getName());
public HttpServer(int port) {
ServerSocket server_socket;
try {
server_socket = new ServerSocket(port);
LOGGER.fine("httpServer running on port " +
server_socket.getLocalPort());
while (true) {
Socket socket = server_socket.accept();
LOGGER.fine("New connection accepted " +
socket.getInetAddress() +
":" + socket.getPort());
try {
HttpRequestHandler request =
new HttpRequestHandler(socket);
Thread thread = new Thread(request);
thread.start();
}
catch (Exception e) {
LOGGER.log(Level.FINE, "Exception", e);
}
}
}
catch (IOException e) {
LOGGER.log(Level.FINE, "Exception", e);
}
}
public static void main(String[] args) {
HttpServer httpServer = new HttpServer(Integer.parseInt(args[0]));
}
class HttpRequestHandler implements Runnable {
final static String CRLF = "\r\n";
Socket socket;
InputStream input;
OutputStream output;
BufferedReader br;
public HttpRequestHandler(Socket socket) throws Exception {
this.socket = socket;
this.input = socket.getInputStream();
this.output = socket.getOutputStream();
this.br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
public void run() {
try {
processRequest();
}
catch (Exception e) {
e.printStackTrace();
}
}
private void processRequest() throws Exception {
while (true) {
String headerLine = br.readLine();
LOGGER.fine(headerLine);
if (headerLine.equals(CRLF) || headerLine.equals("")) break;
StringTokenizer s = new StringTokenizer(headerLine);
String temp = s.nextToken();
if (temp.equals("GET")) {
String serverLine = "Server: Simple httpServer";
String contentTypeLine = "text";
String entityBody = "";
String contentLengthLine;
String statusLine = "HTTP/1.0 200 OK" + CRLF;
contentLengthLine = "Content-Length: "
+ (new Integer(entityBody.length())).toString() + CRLF;
contentTypeLine = "text/html";
output.write(statusLine.getBytes());
output.write(serverLine.getBytes());
output.write(contentTypeLine.getBytes());
output.write(contentLengthLine.getBytes());
output.write(CRLF.getBytes());
output.write(entityBody.getBytes());
}
}
try {
output.close();
br.close();
socket.close();
}
catch (Exception e) {
// Do Nothing
e.printStackTrace();
}
}
}
}

View file

@ -0,0 +1,415 @@
/**
*
* Copyright the original author or authors
*
* 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.jingleold.nat;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.logging.Logger;
/**
* ICE Transport candidate.
* <p/>
* A candidate represents the possible transport for data interchange between
* the two endpoints.
*
* @author Thiago Camargo
*/
public class ICECandidate extends TransportCandidate implements Comparable<ICECandidate> {
private static final Logger LOGGER = Logger.getLogger(ICECandidate.class.getName());
private String id; // An identification
private String username;
private int preference;
private Protocol proto;
private Channel channel;
private int network;
private Type type;
public enum Type {
relay, srflx, prflx, local, host
}
public ICECandidate() {
super();
}
/**
* Constructor with the basic elements of a transport definition.
*
* @param ip the IP address to use as a local address
* @param generation used to keep track of the candidates
* @param network used for diagnostics (used when the machine has
* several NICs)
* @param password user name, as it is used in ICE
* @param port the port at the candidate IP address
* @param username user name, as it is used in ICE
* @param preference preference for this transportElement, as it is used
* in ICE
* @param type type as defined in ICE-12
*/
public ICECandidate(String ip, int generation, int network,
String password, int port, String username,
int preference, Type type) {
super(ip, port, generation);
proto = Protocol.UDP;
channel = Channel.MYRTPVOICE;
this.network = network;
this.password = password;
this.username = username;
this.preference = preference;
this.type = type;
}
/**
* Get the ID
*
* @return the id
*/
public String getId() {
return id;
}
/**
* Set the ID
*
* @param id the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* Get the protocol used for the transmission
*
* @return the protocol used for transmission
*/
public Protocol getProto() {
return proto;
}
/**
* Set the protocol for the transmission
*
* @param proto the protocol to use
*/
public void setProto(Protocol proto) {
this.proto = proto;
}
/**
* Get the network interface used for this connection
*
* @return the interface number
*/
public int getNetwork() {
return network;
}
/**
* Set the interface for this connection
*
* @param network the interface number
*/
public void setNetwork(int network) {
this.network = network;
}
/**
* Get the username for this transportElement in ICE
*
* @return a username string
*/
public String getUsername() {
return username;
}
/**
* Get the channel
*
* @return the channel associated
*/
public Channel getChannel() {
return channel;
}
/**
* Set the channel for this transportElement
*
* @param channel the new channel
*/
public void setChannel(Channel channel) {
this.channel = channel;
}
/**
* Set the username for this transportElement in ICE
*
* @param username the username used in ICE
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Get the preference number for this transportElement
*
* @return the preference for this transportElement
*/
public int getPreference() {
return preference;
}
/**
* Set the preference order for this transportElement
*
* @param preference a number identifying the preference (as defined in
* ICE)
*/
public void setPreference(int preference) {
this.preference = preference;
}
/**
* Get the Candidate Type
*
* @return candidate Type
*/
public Type getType() {
return type;
}
/**
* Set the Candidate Type
*
* @param type candidate type.
*/
public void setType(Type type) {
this.type = type;
}
/**
* Check if a transport candidate is usable. The transport resolver should
* check if the transport candidate the other endpoint has provided is
* usable.
* <p/>
* ICE Candidate can check connectivity using UDP echo Test.
*/
public void check(final List<TransportCandidate> localCandidates) {
//TODO candidate is being checked trigger
//candidatesChecking.add(cand);
final ICECandidate checkingCandidate = this;
Thread checkThread = new Thread(new Runnable() {
public void run() {
final TestResult result = new TestResult();
// Media Proxy don't have Echo features.
// If its a relayed candidate we assumpt that is NOT Valid while other candidates still being checked.
// The negotiator MUST add then in the correct situations
if (getType().equals("relay")) {
triggerCandidateChecked(false);
return;
}
ResultListener resultListener = new ResultListener() {
public void testFinished(TestResult testResult, TransportCandidate candidate) {
if (testResult.isReachable() && checkingCandidate.equals(candidate)) {
result.setResult(true);
LOGGER.fine("Candidate reachable: " + candidate.getIp() + ":" + candidate.getPort() + " from " + getIp() +":" + getPort());
}
}
};
for (TransportCandidate candidate : localCandidates) {
CandidateEcho echo = candidate.getCandidateEcho();
if (echo != null) {
if (candidate instanceof ICECandidate) {
ICECandidate iceCandidate = (ICECandidate) candidate;
if (iceCandidate.getType().equals(getType())) {
try {
echo.addResultListener(resultListener);
InetAddress address = InetAddress.getByName(getIp());
echo.testASync(checkingCandidate, getPassword());
}
catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
}
}
for (int i = 0; i < 10 && !result.isReachable(); i++)
try {
LOGGER.severe("ICE Candidate retry #" + i);
Thread.sleep(400);
}
catch (InterruptedException e) {
e.printStackTrace();
}
for (TransportCandidate candidate : localCandidates) {
CandidateEcho echo = candidate.getCandidateEcho();
if (echo != null) {
echo.removeResultListener(resultListener);
}
}
triggerCandidateChecked(result.isReachable());
//TODO candidate is being checked trigger
//candidatesChecking.remove(cand);
}
}, "Transport candidate check");
checkThread.setName("Transport candidate test");
checkThread.start();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ICECandidate other = (ICECandidate) obj;
if (getChannel() == null) {
if (other.getChannel() != null) {
return false;
}
}
else if (!getChannel().equals(other.getChannel())) {
return false;
}
if (getId() == null) {
if (other.getId() != null) {
return false;
}
}
else if (!getId().equals(other.getId())) {
return false;
}
if (getNetwork() != other.getNetwork()) {
return false;
}
if (getPassword() == null) {
if (other.getPassword() != null) {
return false;
}
}
else if (!getPassword().equals(other.password)) {
return false;
}
if (getPreference() != other.getPreference()) {
return false;
}
if (getProto() == null) {
if (other.getProto() != null) {
return false;
}
}
else if (!getProto().equals(other.getProto())) {
return false;
}
if (getUsername() == null) {
if (other.getUsername() != null) {
return false;
}
}
else if (!getUsername().equals(other.getUsername())) {
return false;
}
if (getIp() == null) {
if (other.getIp() != null) {
return false;
}
}
else if (!getIp().equals(other.getIp())) {
return false;
}
if (getPort() != other.getPort()) {
return false;
}
if (getType() == null) {
if (other.getType() != null) {
return false;
}
}
else if (!getType().equals(other.getType())) {
return false;
}
return true;
}
public boolean isNull() {
if (super.isNull()) {
return true;
}
else if (getProto().isNull()) {
return true;
}
else if (getChannel().isNull()) {
return true;
}
return false;
}
/**
* Compare the to other Transport candidate.
*
* @param arg another Transport candidate
* @return a negative integer, zero, or a positive integer as this
* object is less than, equal to, or greater than the specified
* object
*/
public int compareTo(ICECandidate arg) {
if (getPreference() < arg.getPreference()) {
return -1;
} else if (getPreference() > arg.getPreference()) {
return 1;
}
return 0;
}
}

View file

@ -0,0 +1,279 @@
/**
*
* Copyright 2003-2005 Jive Software.
*
* 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.jingleold.nat;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingleold.JingleSession;
import de.javawi.jstun.test.demo.ice.Candidate;
import de.javawi.jstun.test.demo.ice.ICENegociator;
import de.javawi.jstun.util.UtilityException;
/**
* ICE Resolver for Jingle transport method that results in sending data between two entities using the Interactive Connectivity Establishment (ICE) methodology. (XEP-0176)
* The goal of this resolver is to make possible to establish and manage out-of-band connections between two XMPP entities, even if they are behind Network Address Translators (NATs) or firewalls.
* To use this resolver you must have a STUN Server and be in a non STUN blocked network. Or use a XMPP server with public IP detection Service.
*
* @author Thiago Camargo
*/
public class ICEResolver extends TransportResolver {
private static final Logger LOGGER = Logger.getLogger(ICEResolver.class.getName());
XMPPConnection connection;
Random random = new Random();
long sid;
String server;
int port;
static Map<String, ICENegociator> negociatorsMap = new HashMap<String, ICENegociator>();
//ICENegociator iceNegociator = null;
public ICEResolver(XMPPConnection connection, String server, int port) {
super();
this.connection = connection;
this.server = server;
this.port = port;
this.setType(Type.ice);
}
public void initialize() throws XMPPException {
if (!isResolving() && !isResolved()) {
LOGGER.fine("Initialized");
// Negotiation with a STUN server for a set of interfaces is quite slow, but the results
// never change over then instance of a JVM. To increase connection performance considerably
// we now cache established/initialized negotiators for each STUN server, so that subsequent uses
// of the STUN server are much, much faster.
if (negociatorsMap.get(server) == null) {
ICENegociator iceNegociator = new ICENegociator(server, port, (short) 1);
negociatorsMap.put(server, iceNegociator);
// gather candidates
iceNegociator.gatherCandidateAddresses();
// priorize candidates
iceNegociator.prioritizeCandidates();
}
}
this.setInitialized();
}
public void cancel() throws XMPPException {
}
/**
* Resolve the IP and obtain a valid transport method.
* @throws SmackException
*/
public synchronized void resolve(JingleSession session) throws XMPPException, SmackException {
this.setResolveInit();
for (TransportCandidate candidate : this.getCandidatesList()) {
if (candidate instanceof ICECandidate) {
ICECandidate iceCandidate = (ICECandidate) candidate;
iceCandidate.removeCandidateEcho();
}
}
this.clear();
// Create a transport candidate for each ICE negotiator candidate we have.
ICENegociator iceNegociator = negociatorsMap.get(server);
for (Candidate candidate : iceNegociator.getSortedCandidates())
try {
Candidate.CandidateType type = candidate.getCandidateType();
ICECandidate.Type iceType = ICECandidate.Type.local;
if (type.equals(Candidate.CandidateType.ServerReflexive))
iceType = ICECandidate.Type.srflx;
else if (type.equals(Candidate.CandidateType.PeerReflexive))
iceType = ICECandidate.Type.prflx;
else if (type.equals(Candidate.CandidateType.Relayed))
iceType = ICECandidate.Type.relay;
else
iceType = ICECandidate.Type.host;
// JBW/GW - 17JUL08: Figure out the zero-based NIC number for this candidate.
short nicNum = 0;
try {
Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
short i = 0;
NetworkInterface nic = NetworkInterface.getByInetAddress(candidate.getAddress().getInetAddress());
while(nics.hasMoreElements()) {
NetworkInterface checkNIC = nics.nextElement();
if (checkNIC.equals(nic)) {
nicNum = i;
break;
}
i++;
}
} catch (SocketException e1) {
e1.printStackTrace();
}
TransportCandidate transportCandidate = new ICECandidate(candidate.getAddress().getInetAddress().getHostAddress(), 1, nicNum, String.valueOf(Math.abs(random.nextLong())), candidate.getPort(), "1", candidate.getPriority(), iceType);
transportCandidate.setLocalIp(candidate.getBase().getAddress().getInetAddress().getHostAddress());
transportCandidate.setPort(getFreePort());
try {
transportCandidate.addCandidateEcho(session);
}
catch (SocketException e) {
e.printStackTrace();
}
this.addCandidate(transportCandidate);
LOGGER.fine("Candidate addr: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " Priority:" + candidate.getPriority());
}
catch (UtilityException e) {
e.printStackTrace();
}
catch (UnknownHostException e) {
e.printStackTrace();
}
// Get a Relay Candidate from XMPP Server
if (RTPBridge.serviceAvailable(connection)) {
// try {
String localIp;
int network;
// JBW/GW - 17JUL08: ICENegotiator.getPublicCandidate() always returned null in JSTUN 1.7.0, and now the API doesn't exist in JSTUN 1.7.1
// if (iceNegociator.getPublicCandidate() != null) {
// localIp = iceNegociator.getPublicCandidate().getBase().getAddress().getInetAddress().getHostAddress();
// network = iceNegociator.getPublicCandidate().getNetwork();
// }
// else {
{
localIp = BridgedResolver.getLocalHost();
network = 0;
}
sid = Math.abs(random.nextLong());
RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid));
TransportCandidate localCandidate = new ICECandidate(
rtpBridge.getIp(), 1, network, String.valueOf(Math.abs(random.nextLong())), rtpBridge.getPortA(), "1", 0, ICECandidate.Type.relay);
localCandidate.setLocalIp(localIp);
TransportCandidate remoteCandidate = new ICECandidate(
rtpBridge.getIp(), 1, network, String.valueOf(Math.abs(random.nextLong())), rtpBridge.getPortB(), "1", 0, ICECandidate.Type.relay);
remoteCandidate.setLocalIp(localIp);
localCandidate.setSymmetric(remoteCandidate);
remoteCandidate.setSymmetric(localCandidate);
localCandidate.setPassword(rtpBridge.getPass());
remoteCandidate.setPassword(rtpBridge.getPass());
localCandidate.setSessionId(rtpBridge.getSid());
remoteCandidate.setSessionId(rtpBridge.getSid());
localCandidate.setConnection(this.connection);
remoteCandidate.setConnection(this.connection);
addCandidate(localCandidate);
// }
// catch (UtilityException e) {
// e.printStackTrace();
// }
// catch (UnknownHostException e) {
// e.printStackTrace();
// }
// Get Public Candidate From XMPP Server
// JBW/GW - 17JUL08 - ICENegotiator.getPublicCandidate() always returned null in JSTUN 1.7.0, and now it doesn't exist in JSTUN 1.7.1
// if (iceNegociator.getPublicCandidate() == null) {
if (true) {
String publicIp = RTPBridge.getPublicIP(connection);
if (publicIp != null && !publicIp.equals("")) {
Enumeration<NetworkInterface> ifaces = null;
try {
ifaces = NetworkInterface.getNetworkInterfaces();
}
catch (SocketException e) {
e.printStackTrace();
}
// If detect this address in local machine, don't use it.
boolean found = false;
while (ifaces.hasMoreElements() && !false) {
NetworkInterface iface = ifaces.nextElement();
Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = iaddresses.nextElement();
if (iaddress.getHostAddress().indexOf(publicIp) > -1) {
found = true;
break;
}
}
}
if (!found) {
try {
TransportCandidate publicCandidate = new ICECandidate(
publicIp, 1, 0, String.valueOf(Math.abs(random.nextLong())), getFreePort(), "1", 0, ICECandidate.Type.srflx);
publicCandidate.setLocalIp(InetAddress.getLocalHost().getHostAddress());
try {
publicCandidate.addCandidateEcho(session);
}
catch (SocketException e) {
e.printStackTrace();
}
addCandidate(publicCandidate);
}
catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
}
}
this.setResolveEnd();
}
}

Some files were not shown because too many files have changed in this diff Show more