Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java (working copy) @@ -1,92 +0,0 @@ -/** - * $RCSfile: TestMediaSession.java,v $ - * $Revision: 1.1 $ - * $Date: 08/11/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.test; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.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() { - - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java (working copy) @@ -1,93 +0,0 @@ -/** - * $RCSfile: TestMediaManager.java,v $ - * $Revision: 1.3 $ - * $Date: 25/12/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.test; - -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; -import org.jivesoftware.smackx.jingle.nat.TransportCandidate; -import org.jivesoftware.smackx.jingle.JingleSession; - -import java.util.*; - -/** - * 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; - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java (working copy) @@ -1,282 +0,0 @@ -package org.jivesoftware.smackx.jingle.mediaimpl; - -import java.awt.Frame; -import java.awt.TextArea; -import java.awt.Toolkit; -import java.util.Vector; - -import javax.media.Format; -import javax.media.PlugInManager; -import javax.media.Renderer; -import javax.media.format.AudioFormat; - -import org.jivesoftware.smackx.jingle.SmackLogger; - -import com.sun.media.ExclusiveUse; -import com.sun.media.util.Registry; - -public class JMFInit extends Frame implements Runnable { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(JMFInit.class); - - private String tempDir = "/tmp"; - - private boolean done = false; - - private String userHome; - - private boolean visible = false; - - public JMFInit(String[] args, boolean visible) { - super("Initializing JMF..."); - - this.visible = visible; - - Registry.set("secure.allowCaptureFromApplets", true); - Registry.set("secure.allowSaveFileFromApplets", true); - - updateTemp(args); - - try { - Registry.commit(); - } - catch (Exception e) { - - message("Failed to commit to JMFRegistry!"); - } - - Thread detectThread = new Thread(this); - detectThread.run(); - - /* - * int slept = 0; while (!done && slept < 60 * 1000 * 2) { try { - * Thread.currentThread().sleep(500); } catch (InterruptedException ie) { } - * slept += 500; } - * - * if (!done) { console.error("Detection is taking too long! - * Aborting!"); message("Detection is taking too long! Aborting!"); } - * - * try { Thread.currentThread().sleep(2000); } catch - * (InterruptedException ie) { } - */ - } - - public void run() { - detectDirectAudio(); - detectS8DirectAudio(); - detectCaptureDevices(); - done = true; - } - - private void updateTemp(String[] args) { - if (args != null && args.length > 0) { - tempDir = args[0]; - - message("Setting cache directory to " + tempDir); - Registry r = new Registry(); - try { - r.set("secure.cacheDir", tempDir); - r.commit(); - - message("Updated registry"); - } - catch (Exception e) { - message("Couldn't update registry!"); - } - } - } - - private void detectCaptureDevices() { - // check if JavaSound capture is available - message("Looking for Audio capturer"); - Class dsauto; - try { - dsauto = Class.forName("DirectSoundAuto"); - dsauto.newInstance(); - message("Finished detecting DirectSound capturer"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - //Do nothing - } - - Class jsauto; - try { - jsauto = Class.forName("JavaSoundAuto"); - jsauto.newInstance(); - message("Finished detecting javasound capturer"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - message("JavaSound capturer detection failed!"); - } - - /* - // Check if VFWAuto or SunVideoAuto is available - message("Looking for video capture devices"); - Class auto = null; - Class autoPlus = null; - try { - auto = Class.forName("VFWAuto"); - } - catch (Exception e) { - } - if (auto == null) { - try { - auto = Class.forName("SunVideoAuto"); - } - catch (Exception ee) { - - } - try { - autoPlus = Class.forName("SunVideoPlusAuto"); - } - catch (Exception ee) { - - } - } - if (auto == null) { - try { - auto = Class.forName("V4LAuto"); - } - catch (Exception ee) { - - } - } - try { - Object instance = auto.newInstance(); - if (autoPlus != null) { - Object instancePlus = autoPlus.newInstance(); - } - - message("Finished detecting video capture devices"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - - message("Capture device detection failed!"); - } - */ - } - - private void detectDirectAudio() { - Class cls; - int plType = PlugInManager.RENDERER; - String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; - try { - // Check if this is the Windows Performance Pack - hack - cls = Class.forName("VFWAuto"); - // Check if DS capture is supported, otherwise fail DS renderer - // since NT doesn't have capture - cls = Class.forName("com.sun.media.protocol.dsound.DSound"); - // Find the renderer class and instantiate it. - cls = Class.forName(dar); - - Renderer rend = (Renderer) cls.newInstance(); - try { - // Set the format and open the device - AudioFormat af = new AudioFormat(AudioFormat.LINEAR, 44100, 16, - 2); - rend.setInputFormat(af); - rend.open(); - Format[] inputFormats = rend.getSupportedInputFormats(); - // Register the device - PlugInManager.addPlugIn(dar, inputFormats, new Format[0], - plType); - // Move it to the top of the list - Vector rendList = PlugInManager.getPlugInList(null, null, - plType); - int listSize = rendList.size(); - if (rendList.elementAt(listSize - 1).equals(dar)) { - rendList.removeElementAt(listSize - 1); - rendList.insertElementAt(dar, 0); - PlugInManager.setPlugInList(rendList, plType); - PlugInManager.commit(); - // Log.debug("registered"); - } - rend.close(); - } - catch (Throwable t) { - // Log.debug("Error " + t); - } - } - catch (Throwable tt) { - //Do nothing - } - } - - private void detectS8DirectAudio() { - Class cls; - int plType = PlugInManager.RENDERER; - String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; - try { - // Check if this is the solaris Performance Pack - hack - cls = Class.forName("SunVideoAuto"); - - // Find the renderer class and instantiate it. - cls = Class.forName(dar); - - Renderer rend = (Renderer) cls.newInstance(); - - if (rend instanceof ExclusiveUse - && !((ExclusiveUse) rend).isExclusive()) { - // sol8+, DAR supports mixing - Vector rendList = PlugInManager.getPlugInList(null, null, - plType); - int listSize = rendList.size(); - boolean found = false; - String rname = null; - - for (int i = 0; i < listSize; i++) { - rname = (String) (rendList.elementAt(i)); - if (rname.equals(dar)) { // DAR is in the registry - found = true; - rendList.removeElementAt(i); - break; - } - } - - if (found) { - rendList.insertElementAt(dar, 0); - PlugInManager.setPlugInList(rendList, plType); - PlugInManager.commit(); - } - } - } - catch (Throwable tt) { - //Do nothing - } - } - - private void message(String mesg) { - LOGGER.debug(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); - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java (working copy) @@ -1,174 +0,0 @@ -/** - * $RCSfile: Demo.java,v $ - * $Revision: 1.3 $ - * $Date: 28/12/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.demo; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.XMPPConnection; -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 JingleTransportManager transportManager = null; - private Connection 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 XMPPConnection(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") { - 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") { - 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); - } - - } - -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java (working copy) @@ -1,206 +0,0 @@ -/** - * $RCSfile: ScreenShareSession.java,v $ - * $Revision: 1.2 $ - * $Date: 08/11/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.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 javax.swing.JFrame; -import javax.swing.JPanel; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageReceiver; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageTransmitter; -import org.jivesoftware.smackx.jingle.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 SmackLogger LOGGER = SmackLogger.getLogger(ScreenShareSession.class); - - 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.debug("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); - } - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java (working copy) @@ -1,204 +0,0 @@ -package org.jivesoftware.smackx.jingle.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 org.jivesoftware.smackx.jingle.SmackLogger; - -/** - * UDP Image Receiver. - * It uses PNG Tiles into UDP packets. - * - * @author Thiago Rocha Camargo - */ -public class ImageTransmitter implements Runnable { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(ImageTransmitter.class); - - 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.debug("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.error("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.debug("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(); - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java (working copy) @@ -1,13 +0,0 @@ -package org.jivesoftware.smackx.jingle.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); -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java (working copy) @@ -1,223 +0,0 @@ -/* -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.jingle.mediaimpl.sshare.api; - -import java.awt.*; -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; - } - -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java (working copy) @@ -1,53 +0,0 @@ -/* -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.jingle.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); -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java (working copy) @@ -1,24 +0,0 @@ -package org.jivesoftware.smackx.jingle.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; - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java (working copy) @@ -1,178 +0,0 @@ -/* -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.jingle.mediaimpl.sshare.api; - -import java.awt.*; - -/** - * 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..."; - } - -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java (working copy) @@ -1,150 +0,0 @@ -package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; - -import java.awt.*; -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 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(); - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java (working copy) @@ -1,86 +0,0 @@ -/* -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.jingle.mediaimpl.sshare.api; - -import java.awt.*; -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 ); -} - Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java (working copy) @@ -1,98 +0,0 @@ -/* -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.jingle.mediaimpl.sshare.api; - -import java.awt.*; -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; - } - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java (working copy) @@ -1,15 +0,0 @@ -package org.jivesoftware.smackx.jingle.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; -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java (working copy) @@ -1,287 +0,0 @@ -/* -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.jingle.mediaimpl.sshare.api; - -import java.io.PrintStream; -import java.util.Vector; - -import org.jivesoftware.smackx.jingle.SmackLogger; - -/** - * 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 SmackLogger LOGGER = SmackLogger.getLogger(OctTreeQuantizer.class); - - /** - * 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.debug(indentStr + index + ": count=" + count); - else - LOGGER.debug(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[] colorList; - - public OctTreeQuantizer() { - setup(256); - colorList = new Vector[MAX_LEVEL+1]; - for (int i = 0; i < MAX_LEVEL+1; i++) - colorList[i] = new Vector(); - 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.debug("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.debug("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.debug("insertColor failed"); - } - - private void reduceTree(int numColors) { - for (int level = MAX_LEVEL-1; level >= 0; level--) { - Vector v = colorList[level]; - if (v != null && v.size() > 0) { - for (int j = 0; j < v.size(); j++) { - OctTreeNode node = (OctTreeNode)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.debug("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.debug("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; - } - -} - Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java (working copy) @@ -1,16 +0,0 @@ -package org.jivesoftware.smackx.jingle.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); - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java (working copy) @@ -1,115 +0,0 @@ -/** - * $RCSfile: ScreenShareMediaManager.java,v $ - * $Revision: 1.3 $ - * $Date: 25/12/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.sshare; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; -import org.jivesoftware.smackx.jingle.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; - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java (working copy) @@ -1,106 +0,0 @@ -/** - * $RCSfile: MultiMediaManager.java,v $ - * $Revision: 1.3 $ - * $Date: 25/12/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.multi; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; -import org.jivesoftware.smackx.jingle.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; - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java (working copy) @@ -1,165 +0,0 @@ -/** - * $RCSfile: AudioMediaSession.java,v $ - * $Revision: 1.1 $ - * $Date: 08/11/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import java.io.IOException; -import java.net.ServerSocket; - -import javax.media.MediaLocator; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.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 SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); - - 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.debug(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; - } - -} Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java (working copy) @@ -1,171 +0,0 @@ -/** - * $RCSfile: AudioReceiver.java,v $ - * $Revision: 1.1 $ - * $Date: 08/11/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import javax.media.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.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.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 SmackLogger LOGGER = SmackLogger.getLogger(AudioReceiver.class); - - 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.error(" - 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.error(" - Received an RTP PayloadChangeEvent."); - LOGGER.error("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.error(" - Recevied new RTP stream: " + ctl.getFormat()); - } - else - LOGGER.error(" - Recevied new RTP stream"); - - if (participant == null) - LOGGER.error(" The sender of this stream had yet to be identified."); - else { - LOGGER.error(" 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.error("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.error(" - The previously unidentified stream "); - if (ctl != null) - LOGGER.error(" " + ctl.getFormat()); - LOGGER.error(" had now been identified as sent by: " + participant.getCNAME()); - } - } - else if (evt instanceof ByeEvent) { - - LOGGER.error(" - 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.error("Receiver internal error: " + ce); - } - - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java (working copy) @@ -1,170 +0,0 @@ -/** - * $RCSfile: JmfMediaManager.java,v $ - * $Revision: 1.3 $ - * $Date: 08/11/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; -import org.jivesoftware.smackx.jingle.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 SmackLogger LOGGER = SmackLogger.getLogger(JmfMediaManager.class); - - 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.debug("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; - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java (working copy) @@ -1,553 +0,0 @@ -/** - * $RCSfile: AudioChannel.java,v $ - * $Revision: 1.1 $ - * $Date: 08/11/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; - -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.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.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 SmackLogger LOGGER = SmackLogger.getLogger(AudioChannel.class); - - 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.error("Track " + i + " is set to transmit as:"); - LOGGER.error(" " + 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 - * @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.error("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.debug("START"); - } - else { - sendStream.stop(); - LOGGER.debug("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(); - } - - } -} \ No newline at end of file Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java (working copy) @@ -1,55 +0,0 @@ -/** - * $RCSfile: AudioFormatUtils.java,v $ - * $Revision: 1.1 $ - * $Date: 08/11/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import org.jivesoftware.smackx.jingle.media.PayloadType; - -import javax.media.format.AudioFormat; - -/** - * Audio Format Utils. - * - * @author Thiago Camargo - */ -public class AudioFormatUtils { - - /** - * Return a JMF AudioFormat for a given Jingle Payload type. - * Return null if the payload is not supported by this jmf API. - * - * @param payloadtype payloadtype - * @return correspondent audioType - */ - public static AudioFormat getAudioFormat(PayloadType payloadtype) { - - switch (payloadtype.getId()) { - case 0: - return new AudioFormat(AudioFormat.ULAW_RTP); - case 3: - return new AudioFormat(AudioFormat.GSM_RTP); - case 4: - return new AudioFormat(AudioFormat.G723_RTP); - default: - return null; - } - - } - -} Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java (working copy) @@ -1,134 +0,0 @@ -/** - * $RCSfile: SpeexMediaManager.java,v $ - * $Revision: 1.3 $ - * $Date: 25/12/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; -import org.jivesoftware.smackx.jingle.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 SmackLogger LOGGER = SmackLogger.getLogger(SpeexMediaManager.class); - - 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.debug("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; - } -} Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java =================================================================== --- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java (revision 11644) +++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java (working copy) @@ -1,245 +0,0 @@ -/** - * $RCSfile: AudioMediaSession.java,v $ - * $Revision: 1.1 $ - * $Date: 25/12/2006 - * <p/> - * Copyright 2003-2006 Jive Software. - * <p/> - * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p/> - * http://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; - -import java.io.IOException; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.security.GeneralSecurityException; - -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.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.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 SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); - - 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.debug(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.debug("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; - } -}