1Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java 2=================================================================== 3--- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java (revision 11644) 4+++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java (working copy) 5@@ -1,92 +0,0 @@ 6-/** 7- * $RCSfile: TestMediaSession.java,v $ 8- * $Revision: 1.1 $ 9- * $Date: 08/11/2006 10- * <p/> 11- * Copyright 2003-2006 Jive Software. 12- * <p/> 13- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 14- * you may not use this file except in compliance with the License. 15- * You may obtain a copy of the License at 16- * <p/> 17- * http://www.apache.org/licenses/LICENSE-2.0 18- * <p/> 19- * Unless required by applicable law or agreed to in writing, software 20- * distributed under the License is distributed on an "AS IS" BASIS, 21- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22- * See the License for the specific language governing permissions and 23- * limitations under the License. 24- */ 25-package org.jivesoftware.smackx.jingle.mediaimpl.test; 26- 27-import org.jivesoftware.smackx.jingle.JingleSession; 28-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 29-import org.jivesoftware.smackx.jingle.media.PayloadType; 30-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 31- 32-/** 33- * This Class implements a complete JingleMediaSession for unit testing. 34- * 35- * @author Thiago Camargo 36- */ 37-public class TestMediaSession extends JingleMediaSession { 38- 39- /** 40- * Creates a TestMediaSession with defined payload type, remote and local candidates 41- * 42- * @param payloadType Payload of the jmf 43- * @param remote the remote information. The candidate that the jmf will be sent to. 44- * @param local the local information. The candidate that will receive the jmf 45- * @param locator media locator 46- */ 47- public TestMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, 48- final String locator, JingleSession jingleSession) { 49- super(payloadType, remote, local, "Test", jingleSession); 50- initialize(); 51- } 52- 53- /** 54- * Initialize the screen share channels. 55- */ 56- public void initialize() { 57- 58- } 59- 60- /** 61- * Starts transmission and for NAT Traversal reasons start receiving also. 62- */ 63- public void startTrasmit() { 64- 65- } 66- 67- /** 68- * Set transmit activity. If the active is true, the instance should trasmit. 69- * If it is set to false, the instance should pause transmit. 70- * 71- * @param active active state 72- */ 73- public void setTrasmit(boolean active) { 74- 75- } 76- 77- /** 78- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 79- */ 80- public void startReceive() { 81- // Do nothing 82- } 83- 84- /** 85- * Stops transmission and for NAT Traversal reasons stop receiving also. 86- */ 87- public void stopTrasmit() { 88- 89- } 90- 91- /** 92- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 93- */ 94- public void stopReceive() { 95- 96- } 97-} 98Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java 99=================================================================== 100--- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java (revision 11644) 101+++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java (working copy) 102@@ -1,93 +0,0 @@ 103-/** 104- * $RCSfile: TestMediaManager.java,v $ 105- * $Revision: 1.3 $ 106- * $Date: 25/12/2006 107- * <p/> 108- * Copyright 2003-2006 Jive Software. 109- * <p/> 110- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 111- * you may not use this file except in compliance with the License. 112- * You may obtain a copy of the License at 113- * <p/> 114- * http://www.apache.org/licenses/LICENSE-2.0 115- * <p/> 116- * Unless required by applicable law or agreed to in writing, software 117- * distributed under the License is distributed on an "AS IS" BASIS, 118- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 119- * See the License for the specific language governing permissions and 120- * limitations under the License. 121- */ 122- 123-package org.jivesoftware.smackx.jingle.mediaimpl.test; 124- 125-import org.jivesoftware.smackx.jingle.media.JingleMediaManager; 126-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 127-import org.jivesoftware.smackx.jingle.media.PayloadType; 128-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; 129-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 130-import org.jivesoftware.smackx.jingle.JingleSession; 131- 132-import java.util.*; 133- 134-/** 135- * Implements a MediaManager for test purposes. 136- * 137- * @author Thiago Camargo 138- */ 139- 140-public class TestMediaManager extends JingleMediaManager { 141- 142- public static final String MEDIA_NAME = "TestMedia"; 143- 144- private List<PayloadType> payloads = new ArrayList<PayloadType>(); 145- 146- private PayloadType preferredPayloadType = null; 147- 148- public TestMediaManager(JingleTransportManager transportManager) { 149- super(transportManager); 150- } 151- 152- /** 153- * Return all supported Payloads for this Manager. 154- * 155- * @return The Payload List 156- */ 157- public List<PayloadType> getPayloads() { 158- return payloads; 159- } 160- 161- public void setPayloads(List<PayloadType> payloads) { 162- this.payloads.addAll(payloads); 163- } 164- 165- /** 166- * Returns a new JingleMediaSession 167- * 168- * @param payloadType payloadType 169- * @param remote remote Candidate 170- * @param local local Candidate 171- * @return JingleMediaSession JingleMediaSession 172- */ 173- public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, 174- final TransportCandidate local, final JingleSession jingleSession) { 175- TestMediaSession session = null; 176- 177- session = new TestMediaSession(payloadType, remote, local, "", jingleSession); 178- 179- return session; 180- } 181- 182- public PayloadType getPreferredPayloadType() { 183- if (preferredPayloadType != null) 184- return preferredPayloadType; 185- return super.getPreferredPayloadType(); 186- } 187- 188- public void setPreferredPayloadType(PayloadType preferredPayloadType) { 189- this.preferredPayloadType = preferredPayloadType; 190- } 191- 192- public String getName() { 193- return MEDIA_NAME; 194- } 195-} 196Index: org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java 197=================================================================== 198--- org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java (revision 11644) 199+++ org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java (working copy) 200@@ -1,282 +0,0 @@ 201-package org.jivesoftware.smackx.jingle.mediaimpl; 202- 203-import java.awt.Frame; 204-import java.awt.TextArea; 205-import java.awt.Toolkit; 206-import java.util.Vector; 207- 208-import javax.media.Format; 209-import javax.media.PlugInManager; 210-import javax.media.Renderer; 211-import javax.media.format.AudioFormat; 212- 213-import org.jivesoftware.smackx.jingle.SmackLogger; 214- 215-import com.sun.media.ExclusiveUse; 216-import com.sun.media.util.Registry; 217- 218-public class JMFInit extends Frame implements Runnable { 219- 220- private static final SmackLogger LOGGER = SmackLogger.getLogger(JMFInit.class); 221- 222- private String tempDir = "/tmp"; 223- 224- private boolean done = false; 225- 226- private String userHome; 227- 228- private boolean visible = false; 229- 230- public JMFInit(String[] args, boolean visible) { 231- super("Initializing JMF..."); 232- 233- this.visible = visible; 234- 235- Registry.set("secure.allowCaptureFromApplets", true); 236- Registry.set("secure.allowSaveFileFromApplets", true); 237- 238- updateTemp(args); 239- 240- try { 241- Registry.commit(); 242- } 243- catch (Exception e) { 244- 245- message("Failed to commit to JMFRegistry!"); 246- } 247- 248- Thread detectThread = new Thread(this); 249- detectThread.run(); 250- 251- /* 252- * int slept = 0; while (!done && slept < 60 * 1000 * 2) { try { 253- * Thread.currentThread().sleep(500); } catch (InterruptedException ie) { } 254- * slept += 500; } 255- * 256- * if (!done) { console.error("Detection is taking too long! 257- * Aborting!"); message("Detection is taking too long! Aborting!"); } 258- * 259- * try { Thread.currentThread().sleep(2000); } catch 260- * (InterruptedException ie) { } 261- */ 262- } 263- 264- public void run() { 265- detectDirectAudio(); 266- detectS8DirectAudio(); 267- detectCaptureDevices(); 268- done = true; 269- } 270- 271- private void updateTemp(String[] args) { 272- if (args != null && args.length > 0) { 273- tempDir = args[0]; 274- 275- message("Setting cache directory to " + tempDir); 276- Registry r = new Registry(); 277- try { 278- r.set("secure.cacheDir", tempDir); 279- r.commit(); 280- 281- message("Updated registry"); 282- } 283- catch (Exception e) { 284- message("Couldn't update registry!"); 285- } 286- } 287- } 288- 289- private void detectCaptureDevices() { 290- // check if JavaSound capture is available 291- message("Looking for Audio capturer"); 292- Class dsauto; 293- try { 294- dsauto = Class.forName("DirectSoundAuto"); 295- dsauto.newInstance(); 296- message("Finished detecting DirectSound capturer"); 297- } 298- catch (ThreadDeath td) { 299- throw td; 300- } 301- catch (Throwable t) { 302- //Do nothing 303- } 304- 305- Class jsauto; 306- try { 307- jsauto = Class.forName("JavaSoundAuto"); 308- jsauto.newInstance(); 309- message("Finished detecting javasound capturer"); 310- } 311- catch (ThreadDeath td) { 312- throw td; 313- } 314- catch (Throwable t) { 315- message("JavaSound capturer detection failed!"); 316- } 317- 318- /* 319- // Check if VFWAuto or SunVideoAuto is available 320- message("Looking for video capture devices"); 321- Class auto = null; 322- Class autoPlus = null; 323- try { 324- auto = Class.forName("VFWAuto"); 325- } 326- catch (Exception e) { 327- } 328- if (auto == null) { 329- try { 330- auto = Class.forName("SunVideoAuto"); 331- } 332- catch (Exception ee) { 333- 334- } 335- try { 336- autoPlus = Class.forName("SunVideoPlusAuto"); 337- } 338- catch (Exception ee) { 339- 340- } 341- } 342- if (auto == null) { 343- try { 344- auto = Class.forName("V4LAuto"); 345- } 346- catch (Exception ee) { 347- 348- } 349- } 350- try { 351- Object instance = auto.newInstance(); 352- if (autoPlus != null) { 353- Object instancePlus = autoPlus.newInstance(); 354- } 355- 356- message("Finished detecting video capture devices"); 357- } 358- catch (ThreadDeath td) { 359- throw td; 360- } 361- catch (Throwable t) { 362- 363- message("Capture device detection failed!"); 364- } 365- */ 366- } 367- 368- private void detectDirectAudio() { 369- Class cls; 370- int plType = PlugInManager.RENDERER; 371- String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; 372- try { 373- // Check if this is the Windows Performance Pack - hack 374- cls = Class.forName("VFWAuto"); 375- // Check if DS capture is supported, otherwise fail DS renderer 376- // since NT doesn't have capture 377- cls = Class.forName("com.sun.media.protocol.dsound.DSound"); 378- // Find the renderer class and instantiate it. 379- cls = Class.forName(dar); 380- 381- Renderer rend = (Renderer) cls.newInstance(); 382- try { 383- // Set the format and open the device 384- AudioFormat af = new AudioFormat(AudioFormat.LINEAR, 44100, 16, 385- 2); 386- rend.setInputFormat(af); 387- rend.open(); 388- Format[] inputFormats = rend.getSupportedInputFormats(); 389- // Register the device 390- PlugInManager.addPlugIn(dar, inputFormats, new Format[0], 391- plType); 392- // Move it to the top of the list 393- Vector rendList = PlugInManager.getPlugInList(null, null, 394- plType); 395- int listSize = rendList.size(); 396- if (rendList.elementAt(listSize - 1).equals(dar)) { 397- rendList.removeElementAt(listSize - 1); 398- rendList.insertElementAt(dar, 0); 399- PlugInManager.setPlugInList(rendList, plType); 400- PlugInManager.commit(); 401- // Log.debug("registered"); 402- } 403- rend.close(); 404- } 405- catch (Throwable t) { 406- // Log.debug("Error " + t); 407- } 408- } 409- catch (Throwable tt) { 410- //Do nothing 411- } 412- } 413- 414- private void detectS8DirectAudio() { 415- Class cls; 416- int plType = PlugInManager.RENDERER; 417- String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; 418- try { 419- // Check if this is the solaris Performance Pack - hack 420- cls = Class.forName("SunVideoAuto"); 421- 422- // Find the renderer class and instantiate it. 423- cls = Class.forName(dar); 424- 425- Renderer rend = (Renderer) cls.newInstance(); 426- 427- if (rend instanceof ExclusiveUse 428- && !((ExclusiveUse) rend).isExclusive()) { 429- // sol8+, DAR supports mixing 430- Vector rendList = PlugInManager.getPlugInList(null, null, 431- plType); 432- int listSize = rendList.size(); 433- boolean found = false; 434- String rname = null; 435- 436- for (int i = 0; i < listSize; i++) { 437- rname = (String) (rendList.elementAt(i)); 438- if (rname.equals(dar)) { // DAR is in the registry 439- found = true; 440- rendList.removeElementAt(i); 441- break; 442- } 443- } 444- 445- if (found) { 446- rendList.insertElementAt(dar, 0); 447- PlugInManager.setPlugInList(rendList, plType); 448- PlugInManager.commit(); 449- } 450- } 451- } 452- catch (Throwable tt) { 453- //Do nothing 454- } 455- } 456- 457- private void message(String mesg) { 458- LOGGER.debug(mesg); 459- } 460- 461- private void createGUI() { 462- TextArea textBox = new TextArea(5, 50); 463- add("Center", textBox); 464- textBox.setEditable(false); 465- addNotify(); 466- pack(); 467- 468- int scrWidth = (int) Toolkit.getDefaultToolkit().getScreenSize() 469- .getWidth(); 470- int scrHeight = (int) Toolkit.getDefaultToolkit().getScreenSize() 471- .getHeight(); 472- 473- setLocation((scrWidth - getWidth()) / 2, (scrHeight - getHeight()) / 2); 474- 475- setVisible(visible); 476- 477- } 478- 479- public static void start(boolean visible) { 480- new JMFInit(null, visible); 481- } 482-} 483Index: org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java 484=================================================================== 485--- org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java (revision 11644) 486+++ org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java (working copy) 487@@ -1,174 +0,0 @@ 488-/** 489- * $RCSfile: Demo.java,v $ 490- * $Revision: 1.3 $ 491- * $Date: 28/12/2006 492- * <p/> 493- * Copyright 2003-2006 Jive Software. 494- * <p/> 495- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 496- * you may not use this file except in compliance with the License. 497- * You may obtain a copy of the License at 498- * <p/> 499- * http://www.apache.org/licenses/LICENSE-2.0 500- * <p/> 501- * Unless required by applicable law or agreed to in writing, software 502- * distributed under the License is distributed on an "AS IS" BASIS, 503- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 504- * See the License for the specific language governing permissions and 505- * limitations under the License. 506- */ 507-package org.jivesoftware.smackx.jingle.mediaimpl.demo; 508- 509-import org.jivesoftware.smack.Connection; 510-import org.jivesoftware.smack.XMPPConnection; 511-import org.jivesoftware.smack.XMPPException; 512-import org.jivesoftware.smackx.jingle.JingleManager; 513-import org.jivesoftware.smackx.jingle.JingleSession; 514-import org.jivesoftware.smackx.jingle.JingleSessionRequest; 515-import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener; 516-import org.jivesoftware.smackx.jingle.media.JingleMediaManager; 517-import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager; 518-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager; 519-import org.jivesoftware.smackx.jingle.nat.ICETransportManager; 520-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; 521- 522-import javax.swing.*; 523-import java.awt.event.ActionEvent; 524-import java.util.ArrayList; 525-import java.util.List; 526- 527-/** 528- * Jingle Demo Application. It register in a XMPP Server and let users place calls using a full JID and auto-receive calls. 529- * Parameters: Server User Pass. 530- */ 531-public class Demo extends JFrame { 532- 533- private JingleTransportManager transportManager = null; 534- private Connection xmppConnection = null; 535- 536- private String server = null; 537- private String user = null; 538- private String pass = null; 539- 540- private JingleManager jm = null; 541- private JingleSession incoming = null; 542- private JingleSession outgoing = null; 543- 544- private JTextField jid; 545- 546- public Demo(String server, String user, String pass) { 547- 548- this.server = server; 549- this.user = user; 550- this.pass = pass; 551- 552- if (user.equals("jeffw")) { 553- jid = new JTextField("eowyn" + "@" + server + "/Smack"); 554- } else { 555- jid = new JTextField("jeffw" + "@" + server + "/Smack"); 556- } 557- 558- xmppConnection = new XMPPConnection(server); 559- try { 560- xmppConnection.connect(); 561- xmppConnection.login(user, pass); 562- initialize(); 563- } 564- catch (XMPPException e) { 565- e.printStackTrace(); 566- } 567- } 568- 569- public void initialize() { 570- ICETransportManager icetm0 = new ICETransportManager(xmppConnection, "10.47.47.53", 3478); 571- List<JingleMediaManager> mediaManagers = new ArrayList<JingleMediaManager>(); 572- //mediaManagers.add(new JmfMediaManager(icetm0)); 573- mediaManagers.add(new SpeexMediaManager(icetm0)); 574- mediaManagers.add(new ScreenShareMediaManager(icetm0)); 575- jm = new JingleManager(xmppConnection, mediaManagers); 576- jm.addCreationListener(icetm0); 577- 578- jm.addJingleSessionRequestListener(new JingleSessionRequestListener() { 579- public void sessionRequested(JingleSessionRequest request) { 580- 581-// if (incoming != null) 582-// return; 583- 584- try { 585- // Accept the call 586- incoming = request.accept(); 587- 588- // Start the call 589- incoming.startIncoming(); 590- } 591- catch (XMPPException e) { 592- e.printStackTrace(); 593- } 594- 595- } 596- }); 597- createGUI(); 598- } 599- 600- public void createGUI() { 601- 602- JPanel jPanel = new JPanel(); 603- 604- jPanel.add(jid); 605- 606- jPanel.add(new JButton(new AbstractAction("Call") { 607- public void actionPerformed(ActionEvent e) { 608- if (outgoing != null) return; 609- try { 610- outgoing = jm.createOutgoingJingleSession(jid.getText()); 611- outgoing.startOutgoing(); 612- } 613- catch (XMPPException e1) { 614- e1.printStackTrace(); 615- } 616- } 617- })); 618- 619- jPanel.add(new JButton(new AbstractAction("Hangup") { 620- public void actionPerformed(ActionEvent e) { 621- if (outgoing != null) 622- try { 623- outgoing.terminate(); 624- } 625- catch (XMPPException e1) { 626- e1.printStackTrace(); 627- } 628- finally { 629- outgoing = null; 630- } 631- if (incoming != null) 632- try { 633- incoming.terminate(); 634- } 635- catch (XMPPException e1) { 636- e1.printStackTrace(); 637- } 638- finally { 639- incoming = null; 640- } 641- } 642- })); 643- 644- this.add(jPanel); 645- 646- } 647- 648- public static void main(String args[]) { 649- 650- Demo demo = null; 651- 652- if (args.length > 2) { 653- demo = new Demo(args[0], args[1], args[2]); 654- demo.pack(); 655- demo.setVisible(true); 656- demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 657- } 658- 659- } 660- 661-} 662Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java 663=================================================================== 664--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java (revision 11644) 665+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java (working copy) 666@@ -1,206 +0,0 @@ 667-/** 668- * $RCSfile: ScreenShareSession.java,v $ 669- * $Revision: 1.2 $ 670- * $Date: 08/11/2006 671- * <p/> 672- * Copyright 2003-2006 Jive Software. 673- * <p/> 674- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 675- * you may not use this file except in compliance with the License. 676- * You may obtain a copy of the License at 677- * <p/> 678- * http://www.apache.org/licenses/LICENSE-2.0 679- * <p/> 680- * Unless required by applicable law or agreed to in writing, software 681- * distributed under the License is distributed on an "AS IS" BASIS, 682- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 683- * See the License for the specific language governing permissions and 684- * limitations under the License. 685- */ 686-package org.jivesoftware.smackx.jingle.mediaimpl.sshare; 687- 688-import java.awt.Rectangle; 689-import java.awt.event.WindowAdapter; 690-import java.awt.event.WindowEvent; 691-import java.io.IOException; 692-import java.net.DatagramSocket; 693-import java.net.InetAddress; 694-import java.net.ServerSocket; 695-import java.net.UnknownHostException; 696- 697-import javax.swing.JFrame; 698-import javax.swing.JPanel; 699- 700-import org.jivesoftware.smackx.jingle.JingleSession; 701-import org.jivesoftware.smackx.jingle.SmackLogger; 702-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 703-import org.jivesoftware.smackx.jingle.media.PayloadType; 704-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder; 705-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder; 706-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageReceiver; 707-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageTransmitter; 708-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 709- 710-/** 711- * This Class implements a complete JingleMediaSession. 712- * It sould be used to transmit and receive captured images from the Display. 713- * This Class should be automaticly controlled by JingleSession. 714- * For better NAT Traversal support this implementation don't support only receive or only transmit. 715- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() 716- * 717- * @author Thiago Camargo 718- */ 719-public class ScreenShareSession extends JingleMediaSession { 720- 721- private static final SmackLogger LOGGER = SmackLogger.getLogger(ScreenShareSession.class); 722- 723- private ImageTransmitter transmitter = null; 724- private ImageReceiver receiver = null; 725- private int width = 600; 726- private int height = 600; 727- 728- /** 729- * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates 730- * 731- * @param payloadType Payload of the jmf 732- * @param remote the remote information. The candidate that the jmf will be sent to. 733- * @param local the local information. The candidate that will receive the jmf 734- * @param locator media locator 735- */ 736- public ScreenShareSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, 737- final String locator, JingleSession jingleSession) { 738- super(payloadType, remote, local, "Screen", jingleSession); 739- initialize(); 740- } 741- 742- /** 743- * Initialize the screen share channels. 744- */ 745- public void initialize() { 746- 747- JingleSession session = getJingleSession(); 748- if ((session != null) && (session.getInitiator().equals(session.getConnection().getUser()))) { 749- // If the initiator of the jingle session is us then we transmit a screen share. 750- try { 751- InetAddress remote = InetAddress.getByName(getRemote().getIp()); 752- transmitter = new ImageTransmitter(new DatagramSocket(getLocal().getPort()), remote, getRemote().getPort(), 753- new Rectangle(0, 0, width, height)); 754- } catch (Exception e) { 755- e.printStackTrace(); 756- } 757- 758- } else { 759- // Otherwise we receive a screen share. 760- JFrame window = new JFrame(); 761- JPanel jp = new JPanel(); 762- window.add(jp); 763- 764- window.setLocation(0, 0); 765- window.setSize(600, 600); 766- 767- window.addWindowListener(new WindowAdapter() { 768- public void windowClosed(WindowEvent e) { 769- receiver.stop(); 770- } 771- }); 772- 773- try { 774- receiver = new ImageReceiver(InetAddress.getByName("0.0.0.0"), getRemote().getPort(), getLocal().getPort(), width, 775- height); 776- LOGGER.debug("Receiving on:" + receiver.getLocalPort()); 777- } catch (UnknownHostException e) { 778- e.printStackTrace(); 779- } 780- 781- jp.add(receiver); 782- receiver.setVisible(true); 783- window.setAlwaysOnTop(true); 784- window.setVisible(true); 785- } 786- } 787- 788- /** 789- * Starts transmission and for NAT Traversal reasons start receiving also. 790- */ 791- public void startTrasmit() { 792- new Thread(transmitter).start(); 793- } 794- 795- /** 796- * Set transmit activity. If the active is true, the instance should trasmit. 797- * If it is set to false, the instance should pause transmit. 798- * 799- * @param active active state 800- */ 801- public void setTrasmit(boolean active) { 802- transmitter.setTransmit(true); 803- } 804- 805- /** 806- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 807- */ 808- public void startReceive() { 809- // Do nothing 810- } 811- 812- /** 813- * Stops transmission and for NAT Traversal reasons stop receiving also. 814- */ 815- public void stopTrasmit() { 816- if (transmitter != null) { 817- transmitter.stop(); 818- } 819- } 820- 821- /** 822- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 823- */ 824- public void stopReceive() { 825- if (receiver != null) { 826- receiver.stop(); 827- } 828- } 829- 830- /** 831- * Obtain a free port we can use. 832- * 833- * @return A free port number. 834- */ 835- protected int getFreePort() { 836- ServerSocket ss; 837- int freePort = 0; 838- 839- for (int i = 0; i < 10; i++) { 840- freePort = (int) (10000 + Math.round(Math.random() * 10000)); 841- freePort = freePort % 2 == 0 ? freePort : freePort + 1; 842- try { 843- ss = new ServerSocket(freePort); 844- freePort = ss.getLocalPort(); 845- ss.close(); 846- return freePort; 847- } catch (IOException e) { 848- e.printStackTrace(); 849- } 850- } 851- try { 852- ss = new ServerSocket(0); 853- freePort = ss.getLocalPort(); 854- ss.close(); 855- } catch (IOException e) { 856- e.printStackTrace(); 857- } 858- return freePort; 859- } 860- 861- public void setEncoder(ImageEncoder encoder) { 862- if (encoder != null) { 863- this.transmitter.setEncoder(encoder); 864- } 865- } 866- 867- public void setDecoder(ImageDecoder decoder) { 868- if (decoder != null) { 869- this.receiver.setDecoder(decoder); 870- } 871- } 872-} 873Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java 874=================================================================== 875--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java (revision 11644) 876+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java (working copy) 877@@ -1,204 +0,0 @@ 878-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 879- 880-import java.awt.AWTException; 881-import java.awt.Rectangle; 882-import java.awt.Robot; 883-import java.awt.image.BufferedImage; 884-import java.awt.image.PixelGrabber; 885-import java.io.ByteArrayOutputStream; 886-import java.io.IOException; 887-import java.net.DatagramPacket; 888-import java.net.DatagramSocket; 889-import java.net.InetAddress; 890-import java.util.Arrays; 891- 892-import org.jivesoftware.smackx.jingle.SmackLogger; 893- 894-/** 895- * UDP Image Receiver. 896- * It uses PNG Tiles into UDP packets. 897- * 898- * @author Thiago Rocha Camargo 899- */ 900-public class ImageTransmitter implements Runnable { 901- 902- private static final SmackLogger LOGGER = SmackLogger.getLogger(ImageTransmitter.class); 903- 904- private Robot robot; 905- private InetAddress localHost; 906- private InetAddress remoteHost; 907- private int localPort; 908- private int remotePort; 909- public static final int tileWidth = 25; 910- private boolean on = true; 911- private boolean transmit = false; 912- private DatagramSocket socket; 913- private Rectangle area; 914- private int tiles[][][]; 915- private int maxI; 916- private int maxJ; 917- private ImageEncoder encoder; 918- public final static int KEYFRAME = 10; 919- 920- public ImageTransmitter(DatagramSocket socket, InetAddress remoteHost, int remotePort, Rectangle area) { 921- 922- try { 923- robot = new Robot(); 924- 925- maxI = (int) Math.ceil(area.getWidth() / tileWidth); 926- maxJ = (int) Math.ceil(area.getHeight() / tileWidth); 927- 928- tiles = new int[maxI][maxJ][tileWidth * tileWidth]; 929- 930- this.area = area; 931- this.socket = socket; 932- localHost = socket.getLocalAddress(); 933- localPort = socket.getLocalPort(); 934- this.remoteHost = remoteHost; 935- this.remotePort = remotePort; 936- this.encoder = new DefaultEncoder(); 937- 938- transmit = true; 939- 940- } 941- catch (AWTException e) { 942- e.printStackTrace(); 943- } 944- 945- } 946- 947- public void start() { 948- byte buf[] = new byte[1024]; 949- final DatagramPacket p = new DatagramPacket(buf, 1024); 950- 951- int keyframe = 0; 952- 953- while (on) { 954- if (transmit) { 955- 956- BufferedImage capture = robot.createScreenCapture(area); 957- 958- QuantizeFilter filter = new QuantizeFilter(); 959- capture = filter.filter(capture, null); 960- 961- long trace = System.currentTimeMillis(); 962- 963- if (++keyframe > KEYFRAME) { 964- keyframe = 0; 965- } 966- LOGGER.debug("KEYFRAME:" + keyframe); 967- 968- for (int i = 0; i < maxI; i++) { 969- for (int j = 0; j < maxJ; j++) { 970- 971- final BufferedImage bufferedImage = capture.getSubimage(i * tileWidth, j * tileWidth, tileWidth, tileWidth); 972- 973- int pixels[] = new int[tileWidth * tileWidth]; 974- 975- PixelGrabber pg = new PixelGrabber(bufferedImage, 0, 0, tileWidth, tileWidth, pixels, 0, tileWidth); 976- 977- try { 978- if (pg.grabPixels()) { 979- 980- if (keyframe == KEYFRAME || !Arrays.equals(tiles[i][j], pixels)) { 981- 982- ByteArrayOutputStream baos = encoder.encode(bufferedImage); 983- 984- if (baos != null) { 985- 986- try { 987- 988- Thread.sleep(1); 989- 990- baos.write(i); 991- baos.write(j); 992- 993- byte[] bytesOut = baos.toByteArray(); 994- 995- if (bytesOut.length > 1000) 996- LOGGER.error("Bytes out > 1000. Equals " + bytesOut.length); 997- 998- p.setData(bytesOut); 999- p.setAddress(remoteHost); 1000- p.setPort(remotePort); 1001- 1002- try { 1003- socket.send(p); 1004- } 1005- catch (IOException e) { 1006- e.printStackTrace(); 1007- } 1008- 1009- tiles[i][j] = pixels; 1010- 1011- } 1012- catch (Exception e) { 1013- } 1014- 1015- } 1016- 1017- } 1018- 1019- } 1020- } 1021- catch (InterruptedException e) { 1022- e.printStackTrace(); 1023- } 1024- } 1025- } 1026- 1027- trace = (System.currentTimeMillis() - trace); 1028- LOGGER.debug("Loop Time:" + trace); 1029- 1030- if (trace < 500) { 1031- try { 1032- Thread.sleep(500 - trace); 1033- } 1034- catch (InterruptedException e) { 1035- e.printStackTrace(); 1036- } 1037- } 1038- } 1039- } 1040- } 1041- 1042- public void run() { 1043- start(); 1044- } 1045- 1046- /** 1047- * Set Transmit Enabled/Disabled 1048- * 1049- * @param transmit boolean Enabled/Disabled 1050- */ 1051- public void setTransmit(boolean transmit) { 1052- this.transmit = transmit; 1053- } 1054- 1055- /** 1056- * Get the encoder used to encode Images Tiles 1057- * 1058- * @return encoder 1059- */ 1060- public ImageEncoder getEncoder() { 1061- return encoder; 1062- } 1063- 1064- /** 1065- * Set the encoder used to encode Image Tiles 1066- * 1067- * @param encoder encoder 1068- */ 1069- public void setEncoder(ImageEncoder encoder) { 1070- this.encoder = encoder; 1071- } 1072- 1073- /** 1074- * Stops Transmitter 1075- */ 1076- public void stop() { 1077- this.transmit = false; 1078- this.on = false; 1079- socket.close(); 1080- } 1081-} 1082Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java 1083=================================================================== 1084--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java (revision 11644) 1085+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java (working copy) 1086@@ -1,13 +0,0 @@ 1087-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1088- 1089-import java.awt.image.BufferedImage; 1090-import java.io.ByteArrayOutputStream; 1091- 1092-/** 1093- * Image Encoder Interface use this interface if you want to change the default encoder 1094- * 1095- * @author Thiago Rocha Camargo 1096- */ 1097-public interface ImageEncoder { 1098- public ByteArrayOutputStream encode(BufferedImage bufferedImage); 1099-} 1100Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java 1101=================================================================== 1102--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java (revision 11644) 1103+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java (working copy) 1104@@ -1,223 +0,0 @@ 1105-/* 1106-Copyright 2006 Jerry Huxtable 1107- 1108-Licensed under the Apache License, Version 2.0 (the "License"); 1109-you may not use this file except in compliance with the License. 1110-You may obtain a copy of the License at 1111- 1112- http://www.apache.org/licenses/LICENSE-2.0 1113- 1114-Unless required by applicable law or agreed to in writing, software 1115-distributed under the License is distributed on an "AS IS" BASIS, 1116-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1117-See the License for the specific language governing permissions and 1118-limitations under the License. 1119-*/ 1120- 1121-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1122- 1123-import java.awt.*; 1124-import java.util.Random; 1125- 1126-/** 1127- * Some more useful math functions for image processing. 1128- * These are becoming obsolete as we move to Java2D. Use MiscComposite instead. 1129- */ 1130-public class PixelUtils { 1131- 1132- public final static int REPLACE = 0; 1133- public final static int NORMAL = 1; 1134- public final static int MIN = 2; 1135- public final static int MAX = 3; 1136- public final static int ADD = 4; 1137- public final static int SUBTRACT = 5; 1138- public final static int DIFFERENCE = 6; 1139- public final static int MULTIPLY = 7; 1140- public final static int HUE = 8; 1141- public final static int SATURATION = 9; 1142- public final static int VALUE = 10; 1143- public final static int COLOR = 11; 1144- public final static int SCREEN = 12; 1145- public final static int AVERAGE = 13; 1146- public final static int OVERLAY = 14; 1147- public final static int CLEAR = 15; 1148- public final static int EXCHANGE = 16; 1149- public final static int DISSOLVE = 17; 1150- public final static int DST_IN = 18; 1151- public final static int ALPHA = 19; 1152- public final static int ALPHA_TO_GRAY = 20; 1153- 1154- private static Random randomGenerator = new Random(); 1155- 1156- /** 1157- * Clamp a value to the range 0..255 1158- */ 1159- public static int clamp(int c) { 1160- if (c < 0) 1161- return 0; 1162- if (c > 255) 1163- return 255; 1164- return c; 1165- } 1166- 1167- public static int interpolate(int v1, int v2, float f) { 1168- return clamp((int)(v1+f*(v2-v1))); 1169- } 1170- 1171- public static int brightness(int rgb) { 1172- int r = (rgb >> 16) & 0xff; 1173- int g = (rgb >> 8) & 0xff; 1174- int b = rgb & 0xff; 1175- return (r+g+b)/3; 1176- } 1177- 1178- public static boolean nearColors(int rgb1, int rgb2, int tolerance) { 1179- int r1 = (rgb1 >> 16) & 0xff; 1180- int g1 = (rgb1 >> 8) & 0xff; 1181- int b1 = rgb1 & 0xff; 1182- int r2 = (rgb2 >> 16) & 0xff; 1183- int g2 = (rgb2 >> 8) & 0xff; 1184- int b2 = rgb2 & 0xff; 1185- return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance; 1186- } 1187- 1188- private final static float hsb1[] = new float[3];//FIXME-not thread safe 1189- private final static float hsb2[] = new float[3];//FIXME-not thread safe 1190- 1191- // Return rgb1 painted onto rgb2 1192- public static int combinePixels(int rgb1, int rgb2, int op) { 1193- return combinePixels(rgb1, rgb2, op, 0xff); 1194- } 1195- 1196- public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha, int channelMask) { 1197- return (rgb2 & ~channelMask) | combinePixels(rgb1 & channelMask, rgb2, op, extraAlpha); 1198- } 1199- 1200- public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha) { 1201- if (op == REPLACE) 1202- return rgb1; 1203- int a1 = (rgb1 >> 24) & 0xff; 1204- int r1 = (rgb1 >> 16) & 0xff; 1205- int g1 = (rgb1 >> 8) & 0xff; 1206- int b1 = rgb1 & 0xff; 1207- int a2 = (rgb2 >> 24) & 0xff; 1208- int r2 = (rgb2 >> 16) & 0xff; 1209- int g2 = (rgb2 >> 8) & 0xff; 1210- int b2 = rgb2 & 0xff; 1211- 1212- switch (op) { 1213- case NORMAL: 1214- break; 1215- case MIN: 1216- r1 = Math.min(r1, r2); 1217- g1 = Math.min(g1, g2); 1218- b1 = Math.min(b1, b2); 1219- break; 1220- case MAX: 1221- r1 = Math.max(r1, r2); 1222- g1 = Math.max(g1, g2); 1223- b1 = Math.max(b1, b2); 1224- break; 1225- case ADD: 1226- r1 = clamp(r1+r2); 1227- g1 = clamp(g1+g2); 1228- b1 = clamp(b1+b2); 1229- break; 1230- case SUBTRACT: 1231- r1 = clamp(r2-r1); 1232- g1 = clamp(g2-g1); 1233- b1 = clamp(b2-b1); 1234- break; 1235- case DIFFERENCE: 1236- r1 = clamp(Math.abs(r1-r2)); 1237- g1 = clamp(Math.abs(g1-g2)); 1238- b1 = clamp(Math.abs(b1-b2)); 1239- break; 1240- case MULTIPLY: 1241- r1 = clamp(r1*r2/255); 1242- g1 = clamp(g1*g2/255); 1243- b1 = clamp(b1*b2/255); 1244- break; 1245- case DISSOLVE: 1246- if ((randomGenerator.nextInt() & 0xff) <= a1) { 1247- r1 = r2; 1248- g1 = g2; 1249- b1 = b2; 1250- } 1251- break; 1252- case AVERAGE: 1253- r1 = (r1+r2)/2; 1254- g1 = (g1+g2)/2; 1255- b1 = (b1+b2)/2; 1256- break; 1257- case HUE: 1258- case SATURATION: 1259- case VALUE: 1260- case COLOR: 1261- Color.RGBtoHSB(r1, g1, b1, hsb1); 1262- Color.RGBtoHSB(r2, g2, b2, hsb2); 1263- switch (op) { 1264- case HUE: 1265- hsb2[0] = hsb1[0]; 1266- break; 1267- case SATURATION: 1268- hsb2[1] = hsb1[1]; 1269- break; 1270- case VALUE: 1271- hsb2[2] = hsb1[2]; 1272- break; 1273- case COLOR: 1274- hsb2[0] = hsb1[0]; 1275- hsb2[1] = hsb1[1]; 1276- break; 1277- } 1278- rgb1 = Color.HSBtoRGB(hsb2[0], hsb2[1], hsb2[2]); 1279- r1 = (rgb1 >> 16) & 0xff; 1280- g1 = (rgb1 >> 8) & 0xff; 1281- b1 = rgb1 & 0xff; 1282- break; 1283- case SCREEN: 1284- r1 = 255 - ((255 - r1) * (255 - r2)) / 255; 1285- g1 = 255 - ((255 - g1) * (255 - g2)) / 255; 1286- b1 = 255 - ((255 - b1) * (255 - b2)) / 255; 1287- break; 1288- case OVERLAY: 1289- int m, s; 1290- s = 255 - ((255 - r1) * (255 - r2)) / 255; 1291- m = r1 * r2 / 255; 1292- r1 = (s * r1 + m * (255 - r1)) / 255; 1293- s = 255 - ((255 - g1) * (255 - g2)) / 255; 1294- m = g1 * g2 / 255; 1295- g1 = (s * g1 + m * (255 - g1)) / 255; 1296- s = 255 - ((255 - b1) * (255 - b2)) / 255; 1297- m = b1 * b2 / 255; 1298- b1 = (s * b1 + m * (255 - b1)) / 255; 1299- break; 1300- case CLEAR: 1301- r1 = g1 = b1 = 0xff; 1302- break; 1303- case DST_IN: 1304- r1 = clamp((r2*a1)/255); 1305- g1 = clamp((g2*a1)/255); 1306- b1 = clamp((b2*a1)/255); 1307- a1 = clamp((a2*a1)/255); 1308- return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 1309- case ALPHA: 1310- a1 = a1*a2/255; 1311- return (a1 << 24) | (r2 << 16) | (g2 << 8) | b2; 1312- case ALPHA_TO_GRAY: 1313- int na = 255-a1; 1314- return (a1 << 24) | (na << 16) | (na << 8) | na; 1315- } 1316- if (extraAlpha != 0xff || a1 != 0xff) { 1317- a1 = a1*extraAlpha/255; 1318- int a3 = (255-a1)*a2/255; 1319- r1 = clamp((r1*a1+r2*a3)/255); 1320- g1 = clamp((g1*a1+g2*a3)/255); 1321- b1 = clamp((b1*a1+b2*a3)/255); 1322- a1 = clamp(a1+a3); 1323- } 1324- return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 1325- } 1326- 1327-} 1328Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java 1329=================================================================== 1330--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java (revision 11644) 1331+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java (working copy) 1332@@ -1,53 +0,0 @@ 1333-/* 1334-Copyright 2006 Jerry Huxtable 1335- 1336-Licensed under the Apache License, Version 2.0 (the "License"); 1337-you may not use this file except in compliance with the License. 1338-You may obtain a copy of the License at 1339- 1340- http://www.apache.org/licenses/LICENSE-2.0 1341- 1342-Unless required by applicable law or agreed to in writing, software 1343-distributed under the License is distributed on an "AS IS" BASIS, 1344-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1345-See the License for the specific language governing permissions and 1346-limitations under the License. 1347-*/ 1348- 1349-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1350- 1351-/** 1352- * The interface for an image quantizer. The addColor method is called (repeatedly 1353- * if necessary) with all the image pixels. A color table can then be returned by 1354- * calling the buildColorTable method. 1355- */ 1356-public interface Quantizer { 1357- /** 1358- * Initialize the quantizer. This should be called before adding any pixels. 1359- * @param numColors the number of colors we're quantizing to. 1360- */ 1361- public void setup(int numColors); 1362- 1363- /** 1364- * Add pixels to the quantizer. 1365- * @param pixels the array of ARGB pixels 1366- * @param offset the offset into the array 1367- * @param count the count of pixels 1368- */ 1369- public void addPixels(int[] pixels, int offset, int count); 1370- 1371- /** 1372- * Build a color table from the added pixels. 1373- * @return an array of ARGB pixels representing a color table 1374- */ 1375- public int[] buildColorTable(); 1376- 1377- /** 1378- * Using the previously-built color table, return the index into that table for a pixel. 1379- * This is guaranteed to return a valid index - returning the index of a color closer 1380- * to that requested if necessary. 1381- * @param rgb the pixel to find 1382- * @return the pixel's index in the color table 1383- */ 1384- public int getIndexForColor(int rgb); 1385-} 1386Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java 1387=================================================================== 1388--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java (revision 11644) 1389+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java (working copy) 1390@@ -1,24 +0,0 @@ 1391-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1392- 1393-import javax.imageio.ImageIO; 1394-import java.awt.image.BufferedImage; 1395-import java.io.ByteArrayOutputStream; 1396-import java.io.IOException; 1397- 1398-/** 1399- * Implements a default PNG Encoder 1400- */ 1401-public class DefaultEncoder implements ImageEncoder{ 1402- 1403- public ByteArrayOutputStream encode(BufferedImage bufferedImage) { 1404- ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1405- try { 1406- ImageIO.write(bufferedImage, "png", baos); 1407- } 1408- catch (IOException e) { 1409- e.printStackTrace(); 1410- baos = null; 1411- } 1412- return baos; 1413- } 1414-} 1415Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java 1416=================================================================== 1417--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java (revision 11644) 1418+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java (working copy) 1419@@ -1,178 +0,0 @@ 1420-/* 1421-Copyright 2006 Jerry Huxtable 1422- 1423-Licensed under the Apache License, Version 2.0 (the "License"); 1424-you may not use this file except in compliance with the License. 1425-You may obtain a copy of the License at 1426- 1427- http://www.apache.org/licenses/LICENSE-2.0 1428- 1429-Unless required by applicable law or agreed to in writing, software 1430-distributed under the License is distributed on an "AS IS" BASIS, 1431-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1432-See the License for the specific language governing permissions and 1433-limitations under the License. 1434-*/ 1435- 1436-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1437- 1438-import java.awt.*; 1439- 1440-/** 1441- * A filter which quantizes an image to a set number of colors - useful for producing 1442- * images which are to be encoded using an index color model. The filter can perform 1443- * Floyd-Steinberg error-diffusion dithering if required. At present, the quantization 1444- * is done using an octtree algorithm but I eventually hope to add more quantization 1445- * methods such as median cut. Note: at present, the filter produces an image which 1446- * uses the RGB color model (because the application it was written for required it). 1447- * I hope to extend it to produce an IndexColorModel by request. 1448- */ 1449-public class QuantizeFilter extends WholeImageFilter { 1450- 1451- /** 1452- * Floyd-Steinberg dithering matrix. 1453- */ 1454- protected final static int[] matrix = { 1455- 0, 0, 0, 1456- 0, 0, 7, 1457- 3, 5, 1, 1458- }; 1459- private int sum = 3+5+7+1; 1460- 1461- private boolean dither; 1462- private int numColors = 256; 1463- private boolean serpentine = true; 1464- 1465- /** 1466- * Set the number of colors to quantize to. 1467- * @param numColors the number of colors. The default is 256. 1468- */ 1469- public void setNumColors(int numColors) { 1470- this.numColors = Math.min(Math.max(numColors, 8), 256); 1471- } 1472- 1473- /** 1474- * Get the number of colors to quantize to. 1475- * @return the number of colors. 1476- */ 1477- public int getNumColors() { 1478- return numColors; 1479- } 1480- 1481- /** 1482- * Set whether to use dithering or not. If not, the image is posterized. 1483- * @param dither true to use dithering 1484- */ 1485- public void setDither(boolean dither) { 1486- this.dither = dither; 1487- } 1488- 1489- /** 1490- * Return the dithering setting 1491- * @return the current setting 1492- */ 1493- public boolean getDither() { 1494- return dither; 1495- } 1496- 1497- /** 1498- * Set whether to use a serpentine pattern for return or not. This can reduce 'avalanche' artifacts in the output. 1499- * @param serpentine true to use serpentine pattern 1500- */ 1501- public void setSerpentine(boolean serpentine) { 1502- this.serpentine = serpentine; 1503- } 1504- 1505- /** 1506- * Return the serpentine setting 1507- * @return the current setting 1508- */ 1509- public boolean getSerpentine() { 1510- return serpentine; 1511- } 1512- 1513- public void quantize(int[] inPixels, int[] outPixels, int width, int height, int numColors, boolean dither, boolean serpentine) { 1514- int count = width*height; 1515- Quantizer quantizer = new OctTreeQuantizer(); 1516- quantizer.setup(numColors); 1517- quantizer.addPixels(inPixels, 0, count); 1518- int[] table = quantizer.buildColorTable(); 1519- 1520- if (!dither) { 1521- for (int i = 0; i < count; i++) 1522- outPixels[i] = table[quantizer.getIndexForColor(inPixels[i])]; 1523- } else { 1524- int index = 0; 1525- for (int y = 0; y < height; y++) { 1526- boolean reverse = serpentine && (y & 1) == 1; 1527- int direction; 1528- if (reverse) { 1529- index = y*width+width-1; 1530- direction = -1; 1531- } else { 1532- index = y*width; 1533- direction = 1; 1534- } 1535- for (int x = 0; x < width; x++) { 1536- int rgb1 = inPixels[index]; 1537- int rgb2 = table[quantizer.getIndexForColor(rgb1)]; 1538- 1539- outPixels[index] = rgb2; 1540- 1541- int r1 = (rgb1 >> 16) & 0xff; 1542- int g1 = (rgb1 >> 8) & 0xff; 1543- int b1 = rgb1 & 0xff; 1544- 1545- int r2 = (rgb2 >> 16) & 0xff; 1546- int g2 = (rgb2 >> 8) & 0xff; 1547- int b2 = rgb2 & 0xff; 1548- 1549- int er = r1-r2; 1550- int eg = g1-g2; 1551- int eb = b1-b2; 1552- 1553- for (int i = -1; i <= 1; i++) { 1554- int iy = i+y; 1555- if (0 <= iy && iy < height) { 1556- for (int j = -1; j <= 1; j++) { 1557- int jx = j+x; 1558- if (0 <= jx && jx < width) { 1559- int w; 1560- if (reverse) 1561- w = matrix[(i+1)*3-j+1]; 1562- else 1563- w = matrix[(i+1)*3+j+1]; 1564- if (w != 0) { 1565- int k = reverse ? index - j : index + j; 1566- rgb1 = inPixels[k]; 1567- r1 = (rgb1 >> 16) & 0xff; 1568- g1 = (rgb1 >> 8) & 0xff; 1569- b1 = rgb1 & 0xff; 1570- r1 += er * w/sum; 1571- g1 += eg * w/sum; 1572- b1 += eb * w/sum; 1573- inPixels[k] = (PixelUtils.clamp(r1) << 16) | (PixelUtils.clamp(g1) << 8) | PixelUtils.clamp(b1); 1574- } 1575- } 1576- } 1577- } 1578- } 1579- index += direction; 1580- } 1581- } 1582- } 1583- } 1584- 1585- protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 1586- int[] outPixels = new int[width*height]; 1587- 1588- quantize(inPixels, outPixels, width, height, numColors, dither, serpentine); 1589- 1590- return outPixels; 1591- } 1592- 1593- public String toString() { 1594- return "Colors/Quantize..."; 1595- } 1596- 1597-} 1598Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java 1599=================================================================== 1600--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java (revision 11644) 1601+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java (working copy) 1602@@ -1,150 +0,0 @@ 1603-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1604- 1605-import java.awt.*; 1606-import java.awt.image.BufferedImage; 1607-import java.io.ByteArrayInputStream; 1608-import java.io.IOException; 1609-import java.net.DatagramPacket; 1610-import java.net.DatagramSocket; 1611-import java.net.InetAddress; 1612-import java.net.SocketException; 1613- 1614-/** 1615- * UDP Image Receiver. 1616- * It uses PNG Tiles into UDP packets. 1617- * 1618- * @author Thiago Rocha Camargo 1619- */ 1620-public class ImageReceiver extends Canvas { 1621- 1622- private boolean on = true; 1623- private DatagramSocket socket; 1624- private BufferedImage tiles[][]; 1625- private static final int tileWidth = ImageTransmitter.tileWidth; 1626- private InetAddress localHost; 1627- private InetAddress remoteHost; 1628- private int localPort; 1629- private int remotePort; 1630- private ImageDecoder decoder; 1631- 1632- public ImageReceiver(final InetAddress remoteHost, final int remotePort, final int localPort, int width, int height) { 1633- tiles = new BufferedImage[width][height]; 1634- 1635- try { 1636- 1637- socket = new DatagramSocket(localPort); 1638- localHost = socket.getLocalAddress(); 1639- this.remoteHost = remoteHost; 1640- this.remotePort = remotePort; 1641- this.localPort = localPort; 1642- this.decoder = new DefaultDecoder(); 1643- 1644- new Thread(new Runnable() { 1645- public void run() { 1646- byte buf[] = new byte[1024]; 1647- DatagramPacket p = new DatagramPacket(buf, 1024); 1648- try { 1649- while (on) { 1650- socket.receive(p); 1651- 1652- int length = p.getLength(); 1653- 1654- BufferedImage bufferedImage = decoder.decode(new ByteArrayInputStream(p.getData(), 0, length - 2)); 1655- 1656- if (bufferedImage != null) { 1657- 1658- int x = p.getData()[length - 2]; 1659- int y = p.getData()[length - 1]; 1660- 1661- drawTile(x, y, bufferedImage); 1662- 1663- } 1664- 1665- } 1666- } 1667- catch (IOException e) { 1668- e.printStackTrace(); 1669- } 1670- } 1671- }).start(); 1672- 1673- new Thread(new Runnable() { 1674- public void run() { 1675- byte buf[] = new byte[1024]; 1676- DatagramPacket p = new DatagramPacket(buf, 1024); 1677- try { 1678- while (on) { 1679- 1680- p.setAddress(remoteHost); 1681- p.setPort(remotePort); 1682- socket.send(p); 1683- 1684- try { 1685- Thread.sleep(1000); 1686- } 1687- catch (InterruptedException e) { 1688- e.printStackTrace(); 1689- } 1690- 1691- } 1692- } 1693- catch (IOException e) { 1694- e.printStackTrace(); 1695- } 1696- } 1697- }).start(); 1698- 1699- } 1700- catch (SocketException e) { 1701- e.printStackTrace(); 1702- } 1703- this.setSize(width, height); 1704- } 1705- 1706- public InetAddress getLocalHost() { 1707- return localHost; 1708- } 1709- 1710- public InetAddress getRemoteHost() { 1711- return remoteHost; 1712- } 1713- 1714- public int getLocalPort() { 1715- return localPort; 1716- } 1717- 1718- public int getRemotePort() { 1719- return remotePort; 1720- } 1721- 1722- public DatagramSocket getDatagramSocket() { 1723- return socket; 1724- } 1725- 1726- public void drawTile(int x, int y, BufferedImage bufferedImage) { 1727- tiles[x][y] = bufferedImage; 1728- //repaint(x * tileWidth, y * tileWidth, tileWidth, tileWidth); 1729- this.getGraphics().drawImage(bufferedImage, tileWidth * x, tileWidth * y, this); 1730- } 1731- 1732- public void paint(Graphics g) { 1733- for (int i = 0; i < tiles.length; i++) { 1734- for (int j = 0; j < tiles[0].length; j++) { 1735- g.drawImage(tiles[i][j], tileWidth * i, tileWidth * j, this); 1736- } 1737- } 1738- } 1739- 1740- public ImageDecoder getDecoder() { 1741- return decoder; 1742- } 1743- 1744- public void setDecoder(ImageDecoder decoder) { 1745- this.decoder = decoder; 1746- } 1747- 1748- public void stop(){ 1749- this.on=false; 1750- socket.close(); 1751- } 1752-} 1753Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java 1754=================================================================== 1755--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java (revision 11644) 1756+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java (working copy) 1757@@ -1,86 +0,0 @@ 1758-/* 1759-Copyright 2006 Jerry Huxtable 1760- 1761-Licensed under the Apache License, Version 2.0 (the "License"); 1762-you may not use this file except in compliance with the License. 1763-You may obtain a copy of the License at 1764- 1765- http://www.apache.org/licenses/LICENSE-2.0 1766- 1767-Unless required by applicable law or agreed to in writing, software 1768-distributed under the License is distributed on an "AS IS" BASIS, 1769-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1770-See the License for the specific language governing permissions and 1771-limitations under the License. 1772-*/ 1773- 1774-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1775- 1776-import java.awt.*; 1777-import java.awt.image.BufferedImage; 1778-import java.awt.image.ColorModel; 1779-import java.awt.image.WritableRaster; 1780- 1781-/** 1782- * A filter which acts as a superclass for filters which need to have the whole image in memory 1783- * to do their stuff. 1784- */ 1785-public abstract class WholeImageFilter extends AbstractBufferedImageOp { 1786- 1787- /** 1788- * The output image bounds. 1789- */ 1790- protected Rectangle transformedSpace; 1791- 1792- /** 1793- * The input image bounds. 1794- */ 1795- protected Rectangle originalSpace; 1796- 1797- /** 1798- * Construct a WholeImageFilter. 1799- */ 1800- public WholeImageFilter() { 1801- } 1802- 1803- public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 1804- int width = src.getWidth(); 1805- int height = src.getHeight(); 1806- int type = src.getType(); 1807- WritableRaster srcRaster = src.getRaster(); 1808- 1809- originalSpace = new Rectangle(0, 0, width, height); 1810- transformedSpace = new Rectangle(0, 0, width, height); 1811- transformSpace(transformedSpace); 1812- 1813- if ( dst == null ) { 1814- ColorModel dstCM = src.getColorModel(); 1815- dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(transformedSpace.width, transformedSpace.height), dstCM.isAlphaPremultiplied(), null); 1816- } 1817- WritableRaster dstRaster = dst.getRaster(); 1818- 1819- int[] inPixels = getRGB( src, 0, 0, width, height, null ); 1820- inPixels = filterPixels( width, height, inPixels, transformedSpace ); 1821- setRGB( dst, 0, 0, transformedSpace.width, transformedSpace.height, inPixels ); 1822- 1823- return dst; 1824- } 1825- 1826- /** 1827- * Calculate output bounds for given input bounds. 1828- * @param rect input and output rectangle 1829- */ 1830- protected void transformSpace(Rectangle rect) { 1831- } 1832- 1833- /** 1834- * Actually filter the pixels. 1835- * @param width the image width 1836- * @param height the image height 1837- * @param inPixels the image pixels 1838- * @param transformedSpace the output bounds 1839- * @return the output pixels 1840- */ 1841- protected abstract int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ); 1842-} 1843- 1844Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java 1845=================================================================== 1846--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java (revision 11644) 1847+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java (working copy) 1848@@ -1,98 +0,0 @@ 1849-/* 1850-Copyright 2006 Jerry Huxtable 1851- 1852-Licensed under the Apache License, Version 2.0 (the "License"); 1853-you may not use this file except in compliance with the License. 1854-You may obtain a copy of the License at 1855- 1856- http://www.apache.org/licenses/LICENSE-2.0 1857- 1858-Unless required by applicable law or agreed to in writing, software 1859-distributed under the License is distributed on an "AS IS" BASIS, 1860-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1861-See the License for the specific language governing permissions and 1862-limitations under the License. 1863-*/ 1864- 1865-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1866- 1867-import java.awt.*; 1868-import java.awt.geom.Point2D; 1869-import java.awt.geom.Rectangle2D; 1870-import java.awt.image.BufferedImage; 1871-import java.awt.image.BufferedImageOp; 1872-import java.awt.image.ColorModel; 1873- 1874-/** 1875- * A convenience class which implements those methods of BufferedImageOp which are rarely changed. 1876- */ 1877-public abstract class AbstractBufferedImageOp implements BufferedImageOp, Cloneable { 1878- 1879- public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { 1880- if ( dstCM == null ) 1881- dstCM = src.getColorModel(); 1882- return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dstCM.isAlphaPremultiplied(), null); 1883- } 1884- 1885- public Rectangle2D getBounds2D( BufferedImage src ) { 1886- return new Rectangle(0, 0, src.getWidth(), src.getHeight()); 1887- } 1888- 1889- public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) { 1890- if ( dstPt == null ) 1891- dstPt = new Point2D.Double(); 1892- dstPt.setLocation( srcPt.getX(), srcPt.getY() ); 1893- return dstPt; 1894- } 1895- 1896- public RenderingHints getRenderingHints() { 1897- return null; 1898- } 1899- 1900- /** 1901- * A convenience method for getting ARGB pixels from an image. This tries to avoid the performance 1902- * penalty of BufferedImage.getRGB unmanaging the image. 1903- * @param image a BufferedImage object 1904- * @param x the left edge of the pixel block 1905- * @param y the right edge of the pixel block 1906- * @param width the width of the pixel arry 1907- * @param height the height of the pixel arry 1908- * @param pixels the array to hold the returned pixels. May be null. 1909- * @return the pixels 1910- * @see #setRGB 1911- */ 1912- public int[] getRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) { 1913- int type = image.getType(); 1914- if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB ) 1915- return (int [])image.getRaster().getDataElements( x, y, width, height, pixels ); 1916- return image.getRGB( x, y, width, height, pixels, 0, width ); 1917- } 1918- 1919- /** 1920- * A convenience method for setting ARGB pixels in an image. This tries to avoid the performance 1921- * penalty of BufferedImage.setRGB unmanaging the image. 1922- * @param image a BufferedImage object 1923- * @param x the left edge of the pixel block 1924- * @param y the right edge of the pixel block 1925- * @param width the width of the pixel arry 1926- * @param height the height of the pixel arry 1927- * @param pixels the array of pixels to set 1928- * @see #getRGB 1929- */ 1930- public void setRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) { 1931- int type = image.getType(); 1932- if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB ) 1933- image.getRaster().setDataElements( x, y, width, height, pixels ); 1934- else 1935- image.setRGB( x, y, width, height, pixels, 0, width ); 1936- } 1937- 1938- public Object clone() { 1939- try { 1940- return super.clone(); 1941- } 1942- catch ( CloneNotSupportedException e ) { 1943- return null; 1944- } 1945- } 1946-} 1947Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java 1948=================================================================== 1949--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java (revision 11644) 1950+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java (working copy) 1951@@ -1,15 +0,0 @@ 1952-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1953- 1954-import java.awt.image.BufferedImage; 1955-import java.io.ByteArrayInputStream; 1956-import java.io.IOException; 1957- 1958-/** 1959- * Image Decoder Interface use this interface if you want to change the default decoder 1960- * 1961- * @author Thiago Rocha Camargo 1962- */ 1963-public interface ImageDecoder { 1964- 1965- public BufferedImage decode(ByteArrayInputStream stream) throws IOException; 1966-} 1967Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java 1968=================================================================== 1969--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java (revision 11644) 1970+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java (working copy) 1971@@ -1,287 +0,0 @@ 1972-/* 1973-Copyright 2006 Jerry Huxtable 1974- 1975-Licensed under the Apache License, Version 2.0 (the "License"); 1976-you may not use this file except in compliance with the License. 1977-You may obtain a copy of the License at 1978- 1979- http://www.apache.org/licenses/LICENSE-2.0 1980- 1981-Unless required by applicable law or agreed to in writing, software 1982-distributed under the License is distributed on an "AS IS" BASIS, 1983-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1984-See the License for the specific language governing permissions and 1985-limitations under the License. 1986-*/ 1987- 1988-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 1989- 1990-import java.io.PrintStream; 1991-import java.util.Vector; 1992- 1993-import org.jivesoftware.smackx.jingle.SmackLogger; 1994- 1995-/** 1996- * An image Quantizer based on the Octree algorithm. This is a very basic implementation 1997- * at present and could be much improved by picking the nodes to reduce more carefully 1998- * (i.e. not completely at random) when I get the time. 1999- */ 2000-public class OctTreeQuantizer implements Quantizer { 2001- 2002- private static final SmackLogger LOGGER = SmackLogger.getLogger(OctTreeQuantizer.class); 2003- 2004- /** 2005- * The greatest depth the tree is allowed to reach 2006- */ 2007- final static int MAX_LEVEL = 5; 2008- 2009- /** 2010- * An Octtree node. 2011- */ 2012- class OctTreeNode { 2013- int children; 2014- int level; 2015- OctTreeNode parent; 2016- OctTreeNode leaf[] = new OctTreeNode[8]; 2017- boolean isLeaf; 2018- int count; 2019- int totalRed; 2020- int totalGreen; 2021- int totalBlue; 2022- int index; 2023- 2024- /** 2025- * A debugging method which prints the tree out. 2026- */ 2027- public void list(PrintStream s, int level) { 2028- String indentStr = ""; 2029- for (int i = 0; i < level; i++) 2030- indentStr += " "; 2031- if (count == 0) 2032- LOGGER.debug(indentStr + index + ": count=" + count); 2033- else 2034- LOGGER.debug(indentStr + index + ": count=" + count + " red=" + (totalRed/count) + " green=" + (totalGreen / count) + " blue=" + (totalBlue / count)); 2035- for (int i = 0; i < 8; i++) 2036- if (leaf[i] != null) 2037- leaf[i].list(s, level+2); 2038- } 2039- } 2040- 2041- private int nodes = 0; 2042- private OctTreeNode root; 2043- private int reduceColors; 2044- private int maximumColors; 2045- private int colors = 0; 2046- private Vector[] colorList; 2047- 2048- public OctTreeQuantizer() { 2049- setup(256); 2050- colorList = new Vector[MAX_LEVEL+1]; 2051- for (int i = 0; i < MAX_LEVEL+1; i++) 2052- colorList[i] = new Vector(); 2053- root = new OctTreeNode(); 2054- } 2055- 2056- /** 2057- * Initialize the quantizer. This should be called before adding any pixels. 2058- * @param numColors the number of colors we're quantizing to. 2059- */ 2060- public void setup(int numColors) { 2061- maximumColors = numColors; 2062- reduceColors = Math.max(512, numColors * 2); 2063- } 2064- 2065- /** 2066- * Add pixels to the quantizer. 2067- * @param pixels the array of ARGB pixels 2068- * @param offset the offset into the array 2069- * @param count the count of pixels 2070- */ 2071- public void addPixels(int[] pixels, int offset, int count) { 2072- for (int i = 0; i < count; i++) { 2073- insertColor(pixels[i+offset]); 2074- if (colors > reduceColors) 2075- reduceTree(reduceColors); 2076- } 2077- } 2078- 2079- /** 2080- * Get the color table index for a color. 2081- * @param rgb the color 2082- * @return the index 2083- */ 2084- public int getIndexForColor(int rgb) { 2085- int red = (rgb >> 16) & 0xff; 2086- int green = (rgb >> 8) & 0xff; 2087- int blue = rgb & 0xff; 2088- 2089- OctTreeNode node = root; 2090- 2091- for (int level = 0; level <= MAX_LEVEL; level++) { 2092- OctTreeNode child; 2093- int bit = 0x80 >> level; 2094- 2095- int index = 0; 2096- if ((red & bit) != 0) 2097- index += 4; 2098- if ((green & bit) != 0) 2099- index += 2; 2100- if ((blue & bit) != 0) 2101- index += 1; 2102- 2103- child = node.leaf[index]; 2104- 2105- if (child == null) 2106- return node.index; 2107- else if (child.isLeaf) 2108- return child.index; 2109- else 2110- node = child; 2111- } 2112- LOGGER.debug("getIndexForColor failed"); 2113- return 0; 2114- } 2115- 2116- private void insertColor(int rgb) { 2117- int red = (rgb >> 16) & 0xff; 2118- int green = (rgb >> 8) & 0xff; 2119- int blue = rgb & 0xff; 2120- 2121- OctTreeNode node = root; 2122- 2123-// LOGGER.debug("insertColor="+Integer.toHexString(rgb)); 2124- for (int level = 0; level <= MAX_LEVEL; level++) { 2125- OctTreeNode child; 2126- int bit = 0x80 >> level; 2127- 2128- int index = 0; 2129- if ((red & bit) != 0) 2130- index += 4; 2131- if ((green & bit) != 0) 2132- index += 2; 2133- if ((blue & bit) != 0) 2134- index += 1; 2135- 2136- child = node.leaf[index]; 2137- 2138- if (child == null) { 2139- node.children++; 2140- 2141- child = new OctTreeNode(); 2142- child.parent = node; 2143- node.leaf[index] = child; 2144- node.isLeaf = false; 2145- nodes++; 2146- colorList[level].addElement(child); 2147- 2148- if (level == MAX_LEVEL) { 2149- child.isLeaf = true; 2150- child.count = 1; 2151- child.totalRed = red; 2152- child.totalGreen = green; 2153- child.totalBlue = blue; 2154- child.level = level; 2155- colors++; 2156- return; 2157- } 2158- 2159- node = child; 2160- } else if (child.isLeaf) { 2161- child.count++; 2162- child.totalRed += red; 2163- child.totalGreen += green; 2164- child.totalBlue += blue; 2165- return; 2166- } else 2167- node = child; 2168- } 2169- LOGGER.debug("insertColor failed"); 2170- } 2171- 2172- private void reduceTree(int numColors) { 2173- for (int level = MAX_LEVEL-1; level >= 0; level--) { 2174- Vector v = colorList[level]; 2175- if (v != null && v.size() > 0) { 2176- for (int j = 0; j < v.size(); j++) { 2177- OctTreeNode node = (OctTreeNode)v.elementAt(j); 2178- if (node.children > 0) { 2179- for (int i = 0; i < 8; i++) { 2180- OctTreeNode child = node.leaf[i]; 2181- if (child != null) { 2182- if (!child.isLeaf) 2183- LOGGER.debug("not a leaf!"); 2184- node.count += child.count; 2185- node.totalRed += child.totalRed; 2186- node.totalGreen += child.totalGreen; 2187- node.totalBlue += child.totalBlue; 2188- node.leaf[i] = null; 2189- node.children--; 2190- colors--; 2191- nodes--; 2192- colorList[level+1].removeElement(child); 2193- } 2194- } 2195- node.isLeaf = true; 2196- colors++; 2197- if (colors <= numColors) 2198- return; 2199- } 2200- } 2201- } 2202- } 2203- 2204- LOGGER.debug("Unable to reduce the OctTree"); 2205- } 2206- 2207- /** 2208- * Build the color table. 2209- * @return the color table 2210- */ 2211- public int[] buildColorTable() { 2212- int[] table = new int[colors]; 2213- buildColorTable(root, table, 0); 2214- return table; 2215- } 2216- 2217- /** 2218- * A quick way to use the quantizer. Just create a table the right size and pass in the pixels. 2219- * @param inPixels the input colors 2220- * @param table the output color table 2221- */ 2222- public void buildColorTable(int[] inPixels, int[] table) { 2223- int count = inPixels.length; 2224- maximumColors = table.length; 2225- for (int i = 0; i < count; i++) { 2226- insertColor(inPixels[i]); 2227- if (colors > reduceColors) 2228- reduceTree(reduceColors); 2229- } 2230- if (colors > maximumColors) 2231- reduceTree(maximumColors); 2232- buildColorTable(root, table, 0); 2233- } 2234- 2235- private int buildColorTable(OctTreeNode node, int[] table, int index) { 2236- if (colors > maximumColors) 2237- reduceTree(maximumColors); 2238- 2239- if (node.isLeaf) { 2240- int count = node.count; 2241- table[index] = 0xff000000 | 2242- ((node.totalRed/count) << 16) | 2243- ((node.totalGreen/count) << 8) | 2244- node.totalBlue/count; 2245- node.index = index++; 2246- } else { 2247- for (int i = 0; i < 8; i++) { 2248- if (node.leaf[i] != null) { 2249- node.index = index; 2250- index = buildColorTable(node.leaf[i], table, index); 2251- } 2252- } 2253- } 2254- return index; 2255- } 2256- 2257-} 2258- 2259Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java 2260=================================================================== 2261--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java (revision 11644) 2262+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java (working copy) 2263@@ -1,16 +0,0 @@ 2264-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; 2265- 2266-import javax.imageio.ImageIO; 2267-import java.awt.image.BufferedImage; 2268-import java.io.ByteArrayInputStream; 2269-import java.io.IOException; 2270- 2271-/** 2272- * Implements a default PNG decoder. 2273- */ 2274-public class DefaultDecoder implements ImageDecoder { 2275- 2276- public BufferedImage decode(ByteArrayInputStream stream) throws IOException { 2277- return ImageIO.read(stream); 2278- } 2279-} 2280Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java 2281=================================================================== 2282--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java (revision 11644) 2283+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java (working copy) 2284@@ -1,115 +0,0 @@ 2285-/** 2286- * $RCSfile: ScreenShareMediaManager.java,v $ 2287- * $Revision: 1.3 $ 2288- * $Date: 25/12/2006 2289- * <p/> 2290- * Copyright 2003-2006 Jive Software. 2291- * <p/> 2292- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 2293- * you may not use this file except in compliance with the License. 2294- * You may obtain a copy of the License at 2295- * <p/> 2296- * http://www.apache.org/licenses/LICENSE-2.0 2297- * <p/> 2298- * Unless required by applicable law or agreed to in writing, software 2299- * distributed under the License is distributed on an "AS IS" BASIS, 2300- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2301- * See the License for the specific language governing permissions and 2302- * limitations under the License. 2303- */ 2304- 2305-package org.jivesoftware.smackx.jingle.mediaimpl.sshare; 2306- 2307-import org.jivesoftware.smackx.jingle.JingleSession; 2308-import org.jivesoftware.smackx.jingle.media.JingleMediaManager; 2309-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 2310-import org.jivesoftware.smackx.jingle.media.PayloadType; 2311-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder; 2312-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder; 2313-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; 2314-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 2315- 2316-import java.util.ArrayList; 2317-import java.util.List; 2318- 2319-/** 2320- * Implements a JingleMediaManager for ScreenSharing. 2321- * It currently uses an Audio payload Type. Which needs to be fixed in the next version. 2322- * 2323- * @author Thiago Camargo 2324- */ 2325- 2326-public class ScreenShareMediaManager extends JingleMediaManager { 2327- 2328- public static final String MEDIA_NAME = "ScreenShare"; 2329- 2330- private List<PayloadType> payloads = new ArrayList<PayloadType>(); 2331- 2332- private ImageDecoder decoder = null; 2333- private ImageEncoder encoder = null; 2334- 2335- public ScreenShareMediaManager(JingleTransportManager transportManager) { 2336- super(transportManager); 2337- setupPayloads(); 2338- } 2339- 2340- /** 2341- * Setup API supported Payloads 2342- */ 2343- private void setupPayloads() { 2344- payloads.add(new PayloadType.Audio(30, "sshare")); 2345- } 2346- 2347- /** 2348- * Return all supported Payloads for this Manager. 2349- * 2350- * @return The Payload List 2351- */ 2352- public List<PayloadType> getPayloads() { 2353- return payloads; 2354- } 2355- 2356- /** 2357- * Returns a new JingleMediaSession 2358- * 2359- * @param payloadType payloadType 2360- * @param remote remote Candidate 2361- * @param local local Candidate 2362- * @return JingleMediaSession JingleMediaSession 2363- */ 2364- public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { 2365- ScreenShareSession session = null; 2366- session = new ScreenShareSession(payloadType, remote, local, "Screen", jingleSession); 2367- if (encoder != null) { 2368- session.setEncoder(encoder); 2369- } 2370- if (decoder != null) { 2371- session.setDecoder(decoder); 2372- } 2373- return session; 2374- } 2375- 2376- public PayloadType getPreferredPayloadType() { 2377- return super.getPreferredPayloadType(); 2378- } 2379- 2380- public ImageDecoder getDecoder() { 2381- return decoder; 2382- } 2383- 2384- public void setDecoder(ImageDecoder decoder) { 2385- this.decoder = decoder; 2386- } 2387- 2388- public ImageEncoder getEncoder() { 2389- return encoder; 2390- } 2391- 2392- public void setEncoder(ImageEncoder encoder) { 2393- this.encoder = encoder; 2394- } 2395- 2396- public String getName() { 2397- return MEDIA_NAME; 2398- } 2399-} 2400Index: org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java 2401=================================================================== 2402--- org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java (revision 11644) 2403+++ org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java (working copy) 2404@@ -1,106 +0,0 @@ 2405-/** 2406- * $RCSfile: MultiMediaManager.java,v $ 2407- * $Revision: 1.3 $ 2408- * $Date: 25/12/2006 2409- * <p/> 2410- * Copyright 2003-2006 Jive Software. 2411- * <p/> 2412- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 2413- * you may not use this file except in compliance with the License. 2414- * You may obtain a copy of the License at 2415- * <p/> 2416- * http://www.apache.org/licenses/LICENSE-2.0 2417- * <p/> 2418- * Unless required by applicable law or agreed to in writing, software 2419- * distributed under the License is distributed on an "AS IS" BASIS, 2420- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2421- * See the License for the specific language governing permissions and 2422- * limitations under the License. 2423- */ 2424- 2425-package org.jivesoftware.smackx.jingle.mediaimpl.multi; 2426- 2427-import org.jivesoftware.smackx.jingle.JingleSession; 2428-import org.jivesoftware.smackx.jingle.media.JingleMediaManager; 2429-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 2430-import org.jivesoftware.smackx.jingle.media.PayloadType; 2431-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; 2432-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 2433- 2434-import java.util.ArrayList; 2435-import java.util.List; 2436- 2437-/** 2438- * Implements a MultiMediaManager using other JingleMediaManager implementations. 2439- * It supports every Codecs that JingleMediaManagers added has. 2440- * 2441- * @author Thiago Camargo 2442- */ 2443- 2444-public class MultiMediaManager extends JingleMediaManager { 2445- 2446- public static final String MEDIA_NAME = "Multi"; 2447- 2448- private List<JingleMediaManager> managers = new ArrayList<JingleMediaManager>(); 2449- 2450- private PayloadType preferredPayloadType = null; 2451- 2452- public MultiMediaManager(JingleTransportManager transportManager) { 2453- super(transportManager); 2454- } 2455- 2456- public void addMediaManager(JingleMediaManager manager) { 2457- managers.add(manager); 2458- } 2459- 2460- public void removeMediaManager(JingleMediaManager manager) { 2461- managers.remove(manager); 2462- } 2463- 2464- /** 2465- * Return all supported Payloads for this Manager. 2466- * 2467- * @return The Payload List 2468- */ 2469- public List<PayloadType> getPayloads() { 2470- List<PayloadType> list = new ArrayList<PayloadType>(); 2471- if (preferredPayloadType != null) list.add(preferredPayloadType); 2472- for (JingleMediaManager manager : managers) { 2473- for (PayloadType payloadType : manager.getPayloads()) { 2474- if (!list.contains(payloadType) && !payloadType.equals(preferredPayloadType)) 2475- list.add(payloadType); 2476- } 2477- } 2478- return list; 2479- } 2480- 2481- /** 2482- * Returns a new JingleMediaSession 2483- * 2484- * @param payloadType payloadType 2485- * @param remote remote Candidate 2486- * @param local local Candidate 2487- * @return JingleMediaSession JingleMediaSession 2488- */ 2489- public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { 2490- for (JingleMediaManager manager : managers) { 2491- if (manager.getPayloads().contains(payloadType)) { 2492- return manager.createMediaSession(payloadType, remote, local, jingleSession); 2493- } 2494- } 2495- return null; 2496- } 2497- 2498- public PayloadType getPreferredPayloadType() { 2499- if (preferredPayloadType != null) return preferredPayloadType; 2500- return super.getPreferredPayloadType(); 2501- } 2502- 2503- public void setPreferredPayloadType(PayloadType preferredPayloadType) { 2504- this.preferredPayloadType = preferredPayloadType; 2505- } 2506- 2507- public String getName() { 2508- return MEDIA_NAME; 2509- } 2510-} 2511Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java 2512=================================================================== 2513--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java (revision 11644) 2514+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java (working copy) 2515@@ -1,165 +0,0 @@ 2516-/** 2517- * $RCSfile: AudioMediaSession.java,v $ 2518- * $Revision: 1.1 $ 2519- * $Date: 08/11/2006 2520- * <p/> 2521- * Copyright 2003-2006 Jive Software. 2522- * <p/> 2523- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 2524- * you may not use this file except in compliance with the License. 2525- * You may obtain a copy of the License at 2526- * <p/> 2527- * http://www.apache.org/licenses/LICENSE-2.0 2528- * <p/> 2529- * Unless required by applicable law or agreed to in writing, software 2530- * distributed under the License is distributed on an "AS IS" BASIS, 2531- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2532- * See the License for the specific language governing permissions and 2533- * limitations under the License. 2534- */ 2535- 2536-package org.jivesoftware.smackx.jingle.mediaimpl.jmf; 2537- 2538-import java.io.IOException; 2539-import java.net.ServerSocket; 2540- 2541-import javax.media.MediaLocator; 2542- 2543-import org.jivesoftware.smackx.jingle.JingleSession; 2544-import org.jivesoftware.smackx.jingle.SmackLogger; 2545-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 2546-import org.jivesoftware.smackx.jingle.media.PayloadType; 2547-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 2548- 2549-/** 2550- * This Class implements a complete JingleMediaSession. 2551- * It sould be used to transmit and receive audio captured from the Mic. 2552- * This Class should be automaticly controlled by JingleSession. 2553- * But you could also use in any VOIP application. 2554- * For better NAT Traversal support this implementation don't support only receive or only transmit. 2555- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() 2556- * 2557- * @author Thiago Camargo 2558- */ 2559-public class AudioMediaSession extends JingleMediaSession { 2560- 2561- private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); 2562- 2563- private AudioChannel audioChannel; 2564- 2565- /** 2566- * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates 2567- * 2568- * @param payloadType Payload of the jmf 2569- * @param remote the remote information. The candidate that the jmf will be sent to. 2570- * @param local the local information. The candidate that will receive the jmf 2571- * @param locator media locator 2572- */ 2573- public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, 2574- final TransportCandidate local, String locator, JingleSession jingleSession) { 2575- super(payloadType, remote, local, locator==null?"dsound://":locator,jingleSession); 2576- initialize(); 2577- } 2578- 2579- /** 2580- * Initialize the Audio Channel to make it able to send and receive audio 2581- */ 2582- public void initialize() { 2583- 2584- String ip; 2585- String localIp; 2586- int localPort; 2587- int remotePort; 2588- 2589- if (this.getLocal().getSymmetric() != null) { 2590- ip = this.getLocal().getIp(); 2591- localIp = this.getLocal().getLocalIp(); 2592- localPort = getFreePort(); 2593- remotePort = this.getLocal().getSymmetric().getPort(); 2594- 2595- LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); 2596- 2597- } 2598- else { 2599- ip = this.getRemote().getIp(); 2600- localIp = this.getLocal().getLocalIp(); 2601- localPort = this.getLocal().getPort(); 2602- remotePort = this.getRemote().getPort(); 2603- } 2604- 2605- audioChannel = new AudioChannel(new MediaLocator(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()),this); 2606- } 2607- 2608- /** 2609- * Starts transmission and for NAT Traversal reasons start receiving also. 2610- */ 2611- public void startTrasmit() { 2612- audioChannel.start(); 2613- } 2614- 2615- /** 2616- * Set transmit activity. If the active is true, the instance should trasmit. 2617- * If it is set to false, the instance should pause transmit. 2618- * 2619- * @param active active state 2620- */ 2621- public void setTrasmit(boolean active) { 2622- audioChannel.setTrasmit(active); 2623- } 2624- 2625- /** 2626- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 2627- */ 2628- public void startReceive() { 2629- // Do nothing 2630- } 2631- 2632- /** 2633- * Stops transmission and for NAT Traversal reasons stop receiving also. 2634- */ 2635- public void stopTrasmit() { 2636- if (audioChannel != null) 2637- audioChannel.stop(); 2638- } 2639- 2640- /** 2641- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 2642- */ 2643- public void stopReceive() { 2644- // Do nothing 2645- } 2646- 2647- /** 2648- * Obtain a free port we can use. 2649- * 2650- * @return A free port number. 2651- */ 2652- protected int getFreePort() { 2653- ServerSocket ss; 2654- int freePort = 0; 2655- 2656- for (int i = 0; i < 10; i++) { 2657- freePort = (int) (10000 + Math.round(Math.random() * 10000)); 2658- freePort = freePort % 2 == 0 ? freePort : freePort + 1; 2659- try { 2660- ss = new ServerSocket(freePort); 2661- freePort = ss.getLocalPort(); 2662- ss.close(); 2663- return freePort; 2664- } 2665- catch (IOException e) { 2666- e.printStackTrace(); 2667- } 2668- } 2669- try { 2670- ss = new ServerSocket(0); 2671- freePort = ss.getLocalPort(); 2672- ss.close(); 2673- } 2674- catch (IOException e) { 2675- e.printStackTrace(); 2676- } 2677- return freePort; 2678- } 2679- 2680-} 2681Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java 2682=================================================================== 2683--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java (revision 11644) 2684+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java (working copy) 2685@@ -1,171 +0,0 @@ 2686-/** 2687- * $RCSfile: AudioReceiver.java,v $ 2688- * $Revision: 1.1 $ 2689- * $Date: 08/11/2006 2690- * <p/> 2691- * Copyright 2003-2006 Jive Software. 2692- * <p/> 2693- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 2694- * you may not use this file except in compliance with the License. 2695- * You may obtain a copy of the License at 2696- * <p/> 2697- * http://www.apache.org/licenses/LICENSE-2.0 2698- * <p/> 2699- * Unless required by applicable law or agreed to in writing, software 2700- * distributed under the License is distributed on an "AS IS" BASIS, 2701- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2702- * See the License for the specific language governing permissions and 2703- * limitations under the License. 2704- */ 2705- 2706-package org.jivesoftware.smackx.jingle.mediaimpl.jmf; 2707- 2708-import javax.media.ControllerErrorEvent; 2709-import javax.media.ControllerEvent; 2710-import javax.media.ControllerListener; 2711-import javax.media.Player; 2712-import javax.media.RealizeCompleteEvent; 2713-import javax.media.protocol.DataSource; 2714-import javax.media.rtp.Participant; 2715-import javax.media.rtp.RTPControl; 2716-import javax.media.rtp.ReceiveStream; 2717-import javax.media.rtp.ReceiveStreamListener; 2718-import javax.media.rtp.SessionListener; 2719-import javax.media.rtp.event.ByeEvent; 2720-import javax.media.rtp.event.NewParticipantEvent; 2721-import javax.media.rtp.event.NewReceiveStreamEvent; 2722-import javax.media.rtp.event.ReceiveStreamEvent; 2723-import javax.media.rtp.event.RemotePayloadChangeEvent; 2724-import javax.media.rtp.event.SessionEvent; 2725-import javax.media.rtp.event.StreamMappedEvent; 2726- 2727-import org.jivesoftware.smackx.jingle.SmackLogger; 2728-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 2729- 2730-/** 2731- * This class implements receive methods and listeners to be used in AudioChannel 2732- * 2733- * @author Thiago Camargo 2734- */ 2735-public class AudioReceiver implements ReceiveStreamListener, SessionListener, 2736- ControllerListener { 2737- 2738- private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioReceiver.class); 2739- 2740- boolean dataReceived = false; 2741- 2742- Object dataSync; 2743- JingleMediaSession jingleMediaSession; 2744- 2745- public AudioReceiver(final Object dataSync, final JingleMediaSession jingleMediaSession) { 2746- this.dataSync = dataSync; 2747- this.jingleMediaSession = jingleMediaSession; 2748- } 2749- 2750- /** 2751- * JingleSessionListener. 2752- */ 2753- public synchronized void update(SessionEvent evt) { 2754- if (evt instanceof NewParticipantEvent) { 2755- Participant p = ((NewParticipantEvent) evt).getParticipant(); 2756- LOGGER.error(" - A new participant had just joined: " + p.getCNAME()); 2757- } 2758- } 2759- 2760- /** 2761- * ReceiveStreamListener 2762- */ 2763- public synchronized void update(ReceiveStreamEvent evt) { 2764- 2765- Participant participant = evt.getParticipant(); // could be null. 2766- ReceiveStream stream = evt.getReceiveStream(); // could be null. 2767- 2768- if (evt instanceof RemotePayloadChangeEvent) { 2769- LOGGER.error(" - Received an RTP PayloadChangeEvent."); 2770- LOGGER.error("Sorry, cannot handle payload change."); 2771- 2772- } 2773- else if (evt instanceof NewReceiveStreamEvent) { 2774- 2775- try { 2776- stream = evt.getReceiveStream(); 2777- DataSource ds = stream.getDataSource(); 2778- 2779- // Find out the formats. 2780- RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl"); 2781- if (ctl != null) { 2782- LOGGER.error(" - Recevied new RTP stream: " + ctl.getFormat()); 2783- } 2784- else 2785- LOGGER.error(" - Recevied new RTP stream"); 2786- 2787- if (participant == null) 2788- LOGGER.error(" The sender of this stream had yet to be identified."); 2789- else { 2790- LOGGER.error(" The stream comes from: " + participant.getCNAME()); 2791- } 2792- 2793- // create a player by passing datasource to the Media Manager 2794- Player p = javax.media.Manager.createPlayer(ds); 2795- if (p == null) 2796- return; 2797- 2798- p.addControllerListener(this); 2799- p.realize(); 2800- jingleMediaSession.mediaReceived(participant != null ? participant.getCNAME() : ""); 2801- 2802- // Notify intialize() that a new stream had arrived. 2803- synchronized (dataSync) { 2804- dataReceived = true; 2805- dataSync.notifyAll(); 2806- } 2807- 2808- } 2809- catch (Exception e) { 2810- LOGGER.error("NewReceiveStreamEvent exception " + e.getMessage()); 2811- return; 2812- } 2813- 2814- } 2815- else if (evt instanceof StreamMappedEvent) { 2816- 2817- if (stream != null && stream.getDataSource() != null) { 2818- DataSource ds = stream.getDataSource(); 2819- // Find out the formats. 2820- RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl"); 2821- LOGGER.error(" - The previously unidentified stream "); 2822- if (ctl != null) 2823- LOGGER.error(" " + ctl.getFormat()); 2824- LOGGER.error(" had now been identified as sent by: " + participant.getCNAME()); 2825- } 2826- } 2827- else if (evt instanceof ByeEvent) { 2828- 2829- LOGGER.error(" - Got \"bye\" from: " + participant.getCNAME()); 2830- 2831- } 2832- 2833- } 2834- 2835- /** 2836- * ControllerListener for the Players. 2837- */ 2838- public synchronized void controllerUpdate(ControllerEvent ce) { 2839- 2840- Player p = (Player) ce.getSourceController(); 2841- 2842- if (p == null) 2843- return; 2844- 2845- // Get this when the internal players are realized. 2846- if (ce instanceof RealizeCompleteEvent) { 2847- p.start(); 2848- } 2849- 2850- if (ce instanceof ControllerErrorEvent) { 2851- p.removeControllerListener(this); 2852- LOGGER.error("Receiver internal error: " + ce); 2853- } 2854- 2855- } 2856-} 2857Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java 2858=================================================================== 2859--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java (revision 11644) 2860+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java (working copy) 2861@@ -1,170 +0,0 @@ 2862-/** 2863- * $RCSfile: JmfMediaManager.java,v $ 2864- * $Revision: 1.3 $ 2865- * $Date: 08/11/2006 2866- * <p/> 2867- * Copyright 2003-2006 Jive Software. 2868- * <p/> 2869- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 2870- * you may not use this file except in compliance with the License. 2871- * You may obtain a copy of the License at 2872- * <p/> 2873- * http://www.apache.org/licenses/LICENSE-2.0 2874- * <p/> 2875- * Unless required by applicable law or agreed to in writing, software 2876- * distributed under the License is distributed on an "AS IS" BASIS, 2877- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2878- * See the License for the specific language governing permissions and 2879- * limitations under the License. 2880- */ 2881- 2882-package org.jivesoftware.smackx.jingle.mediaimpl.jmf; 2883- 2884-import java.io.File; 2885-import java.io.IOException; 2886-import java.util.ArrayList; 2887-import java.util.List; 2888- 2889-import org.jivesoftware.smackx.jingle.JingleSession; 2890-import org.jivesoftware.smackx.jingle.SmackLogger; 2891-import org.jivesoftware.smackx.jingle.media.JingleMediaManager; 2892-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 2893-import org.jivesoftware.smackx.jingle.media.PayloadType; 2894-import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; 2895-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; 2896-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 2897- 2898-/** 2899- * Implements a jingleMediaManager using JMF based API. 2900- * It supports GSM and G723 codecs. 2901- * <i>This API only currently works on windows and Mac.</i> 2902- * 2903- * @author Thiago Camargo 2904- */ 2905-public class JmfMediaManager extends JingleMediaManager { 2906- 2907- private static final SmackLogger LOGGER = SmackLogger.getLogger(JmfMediaManager.class); 2908- 2909- public static final String MEDIA_NAME = "JMF"; 2910- 2911- 2912- private List<PayloadType> payloads = new ArrayList<PayloadType>(); 2913- private String mediaLocator = null; 2914- 2915- /** 2916- * Creates a Media Manager instance 2917- */ 2918- public JmfMediaManager(JingleTransportManager transportManager) { 2919- super(transportManager); 2920- setupPayloads(); 2921- } 2922- 2923- /** 2924- * Creates a Media Manager instance 2925- * 2926- * @param mediaLocator Media Locator 2927- */ 2928- public JmfMediaManager(String mediaLocator, JingleTransportManager transportManager) { 2929- super(transportManager); 2930- this.mediaLocator = mediaLocator; 2931- setupPayloads(); 2932- } 2933- 2934- /** 2935- * Returns a new jingleMediaSession 2936- * 2937- * @param payloadType payloadType 2938- * @param remote remote Candidate 2939- * @param local local Candidate 2940- * @return JingleMediaSession 2941- */ 2942- public JingleMediaSession createMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { 2943- return new AudioMediaSession(payloadType, remote, local, mediaLocator, jingleSession); 2944- } 2945- 2946- /** 2947- * Setup API supported Payloads 2948- */ 2949- private void setupPayloads() { 2950- payloads.add(new PayloadType.Audio(3, "gsm")); 2951- payloads.add(new PayloadType.Audio(4, "g723")); 2952- payloads.add(new PayloadType.Audio(0, "PCMU", 16000)); 2953- } 2954- 2955- /** 2956- * Return all supported Payloads for this Manager 2957- * 2958- * @return The Payload List 2959- */ 2960- public List<PayloadType> getPayloads() { 2961- return payloads; 2962- } 2963- 2964- /** 2965- * Return the media locator or null if not defined 2966- * 2967- * @return media locator 2968- */ 2969- public String getMediaLocator() { 2970- return mediaLocator; 2971- } 2972- 2973- /** 2974- * Set the media locator 2975- * 2976- * @param mediaLocator media locator or null to use default 2977- */ 2978- public void setMediaLocator(String mediaLocator) { 2979- this.mediaLocator = mediaLocator; 2980- } 2981- 2982- /** 2983- * Runs JMFInit the first time the application is started so that capture 2984- * devices are properly detected and initialized by JMF. 2985- */ 2986- public static void setupJMF() { 2987- // .jmf is the place where we store the jmf.properties file used 2988- // by JMF. if the directory does not exist or it does not contain 2989- // a jmf.properties file. or if the jmf.properties file has 0 length 2990- // then this is the first time we're running and should continue to 2991- // with JMFInit 2992- String homeDir = System.getProperty("user.home"); 2993- File jmfDir = new File(homeDir, ".jmf"); 2994- String classpath = System.getProperty("java.class.path"); 2995- classpath += System.getProperty("path.separator") 2996- + jmfDir.getAbsolutePath(); 2997- System.setProperty("java.class.path", classpath); 2998- 2999- if (!jmfDir.exists()) 3000- jmfDir.mkdir(); 3001- 3002- File jmfProperties = new File(jmfDir, "jmf.properties"); 3003- 3004- if (!jmfProperties.exists()) { 3005- try { 3006- jmfProperties.createNewFile(); 3007- } 3008- catch (IOException ex) { 3009- LOGGER.debug("Failed to create jmf.properties"); 3010- ex.printStackTrace(); 3011- } 3012- } 3013- 3014- // if we're running on linux checkout that libjmutil.so is where it 3015- // should be and put it there. 3016- runLinuxPreInstall(); 3017- 3018- //if (jmfProperties.length() == 0) { 3019- new JMFInit(null, false); 3020- //} 3021- 3022- } 3023- 3024- private static void runLinuxPreInstall() { 3025- // @TODO Implement Linux Pre-Install 3026- } 3027- 3028- public String getName() { 3029- return MEDIA_NAME; 3030- } 3031-} 3032Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java 3033=================================================================== 3034--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java (revision 11644) 3035+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java (working copy) 3036@@ -1,553 +0,0 @@ 3037-/** 3038- * $RCSfile: AudioChannel.java,v $ 3039- * $Revision: 1.1 $ 3040- * $Date: 08/11/2006 3041- * <p/> 3042- * Copyright 2003-2006 Jive Software. 3043- * <p/> 3044- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 3045- * you may not use this file except in compliance with the License. 3046- * You may obtain a copy of the License at 3047- * <p/> 3048- * http://www.apache.org/licenses/LICENSE-2.0 3049- * <p/> 3050- * Unless required by applicable law or agreed to in writing, software 3051- * distributed under the License is distributed on an "AS IS" BASIS, 3052- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3053- * See the License for the specific language governing permissions and 3054- * limitations under the License. 3055- */ 3056-package org.jivesoftware.smackx.jingle.mediaimpl.jmf; 3057- 3058-import java.io.IOException; 3059-import java.net.InetAddress; 3060-import java.net.UnknownHostException; 3061-import java.util.ArrayList; 3062-import java.util.List; 3063- 3064-import javax.media.Codec; 3065-import javax.media.Controller; 3066-import javax.media.ControllerClosedEvent; 3067-import javax.media.ControllerEvent; 3068-import javax.media.ControllerListener; 3069-import javax.media.Format; 3070-import javax.media.MediaLocator; 3071-import javax.media.NoProcessorException; 3072-import javax.media.Processor; 3073-import javax.media.UnsupportedPlugInException; 3074-import javax.media.control.BufferControl; 3075-import javax.media.control.PacketSizeControl; 3076-import javax.media.control.TrackControl; 3077-import javax.media.format.AudioFormat; 3078-import javax.media.protocol.ContentDescriptor; 3079-import javax.media.protocol.DataSource; 3080-import javax.media.protocol.PushBufferDataSource; 3081-import javax.media.protocol.PushBufferStream; 3082-import javax.media.rtp.InvalidSessionAddressException; 3083-import javax.media.rtp.RTPManager; 3084-import javax.media.rtp.SendStream; 3085-import javax.media.rtp.SessionAddress; 3086- 3087-import org.jivesoftware.smackx.jingle.SmackLogger; 3088-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 3089- 3090-/** 3091- * An Easy to use Audio Channel implemented using JMF. 3092- * It sends and receives jmf for and from desired IPs and ports. 3093- * Also has a rport Symetric behavior for better NAT Traversal. 3094- * It send data from a defined port and receive data in the same port, making NAT binds easier. 3095- * <p/> 3096- * Send from portA to portB and receive from portB in portA. 3097- * <p/> 3098- * Sending 3099- * portA ---> portB 3100- * <p/> 3101- * Receiving 3102- * portB ---> portA 3103- * <p/> 3104- * <i>Transmit and Receive are interdependents. To receive you MUST trasmit. </i> 3105- * 3106- * @author Thiago Camargo 3107- */ 3108-public class AudioChannel { 3109- 3110- private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioChannel.class); 3111- 3112- private MediaLocator locator; 3113- private String localIpAddress; 3114- private String remoteIpAddress; 3115- private int localPort; 3116- private int portBase; 3117- private Format format; 3118- 3119- private Processor processor = null; 3120- private RTPManager rtpMgrs[]; 3121- private DataSource dataOutput = null; 3122- private AudioReceiver audioReceiver; 3123- 3124- private List<SendStream> sendStreams = new ArrayList<SendStream>(); 3125- 3126- private JingleMediaSession jingleMediaSession; 3127- 3128- private boolean started = false; 3129- 3130- /** 3131- * Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://") 3132- * 3133- * @param locator media locator 3134- * @param localIpAddress local IP address 3135- * @param remoteIpAddress remote IP address 3136- * @param localPort local port number 3137- * @param remotePort remote port number 3138- * @param format audio format 3139- */ 3140- public AudioChannel(MediaLocator locator, 3141- String localIpAddress, 3142- String remoteIpAddress, 3143- int localPort, 3144- int remotePort, 3145- Format format, JingleMediaSession jingleMediaSession) { 3146- 3147- this.locator = locator; 3148- this.localIpAddress = localIpAddress; 3149- this.remoteIpAddress = remoteIpAddress; 3150- this.localPort = localPort; 3151- this.portBase = remotePort; 3152- this.format = format; 3153- this.jingleMediaSession = jingleMediaSession; 3154- } 3155- 3156- /** 3157- * Starts the transmission. Returns null if transmission started ok. 3158- * Otherwise it returns a string with the reason why the setup failed. 3159- * Starts receive also. 3160- * 3161- * @return result description 3162- */ 3163- public synchronized String start() { 3164- if (started) return null; 3165- 3166- // Create a processor for the specified jmf locator 3167- String result = createProcessor(); 3168- if (result != null) { 3169- started = false; 3170- } 3171- 3172- // Create an RTP session to transmit the output of the 3173- // processor to the specified IP address and port no. 3174- result = createTransmitter(); 3175- if (result != null) { 3176- processor.close(); 3177- processor = null; 3178- started = false; 3179- } 3180- else { 3181- started = true; 3182- } 3183- 3184- // Start the transmission 3185- processor.start(); 3186- 3187- return null; 3188- } 3189- 3190- /** 3191- * Stops the transmission if already started. 3192- * Stops the receiver also. 3193- */ 3194- public void stop() { 3195- if (!started) return; 3196- synchronized (this) { 3197- try { 3198- started = false; 3199- if (processor != null) { 3200- processor.stop(); 3201- processor = null; 3202- 3203- for (RTPManager rtpMgr : rtpMgrs) { 3204- rtpMgr.removeReceiveStreamListener(audioReceiver); 3205- rtpMgr.removeSessionListener(audioReceiver); 3206- rtpMgr.removeTargets("Session ended."); 3207- rtpMgr.dispose(); 3208- } 3209- 3210- sendStreams.clear(); 3211- 3212- } 3213- } 3214- catch (Exception e) { 3215- e.printStackTrace(); 3216- } 3217- } 3218- } 3219- 3220- private String createProcessor() { 3221- if (locator == null) 3222- return "Locator is null"; 3223- 3224- DataSource ds; 3225- 3226- try { 3227- ds = javax.media.Manager.createDataSource(locator); 3228- } 3229- catch (Exception e) { 3230- // Try JavaSound Locator as a last resort 3231- try { 3232- ds = javax.media.Manager.createDataSource(new MediaLocator("javasound://")); 3233- } 3234- catch (Exception ee) { 3235- return "Couldn't create DataSource"; 3236- } 3237- } 3238- 3239- // Try to create a processor to handle the input jmf locator 3240- try { 3241- processor = javax.media.Manager.createProcessor(ds); 3242- } 3243- catch (NoProcessorException npe) { 3244- npe.printStackTrace(); 3245- return "Couldn't create processor"; 3246- } 3247- catch (IOException ioe) { 3248- ioe.printStackTrace(); 3249- return "IOException creating processor"; 3250- } 3251- 3252- // Wait for it to configure 3253- boolean result = waitForState(processor, Processor.Configured); 3254- if (!result){ 3255- return "Couldn't configure processor"; 3256- } 3257- 3258- // Get the tracks from the processor 3259- TrackControl[] tracks = processor.getTrackControls(); 3260- 3261- // Do we have atleast one track? 3262- if (tracks == null || tracks.length < 1){ 3263- return "Couldn't find tracks in processor"; 3264- } 3265- 3266- // Set the output content descriptor to RAW_RTP 3267- // This will limit the supported formats reported from 3268- // Track.getSupportedFormats to only valid RTP formats. 3269- ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); 3270- processor.setContentDescriptor(cd); 3271- 3272- Format supported[]; 3273- Format chosen = null; 3274- boolean atLeastOneTrack = false; 3275- 3276- // Program the tracks. 3277- for (int i = 0; i < tracks.length; i++) { 3278- if (tracks[i].isEnabled()) { 3279- 3280- supported = tracks[i].getSupportedFormats(); 3281- 3282- if (supported.length > 0) { 3283- for (Format format : supported) { 3284- if (format instanceof AudioFormat) { 3285- if (this.format.matches(format)) 3286- chosen = format; 3287- } 3288- } 3289- if (chosen != null) { 3290- tracks[i].setFormat(chosen); 3291- LOGGER.error("Track " + i + " is set to transmit as:"); 3292- LOGGER.error(" " + chosen); 3293- 3294- if (tracks[i].getFormat() instanceof AudioFormat) { 3295- int packetRate = 20; 3296- PacketSizeControl pktCtrl = (PacketSizeControl) processor.getControl(PacketSizeControl.class.getName()); 3297- if (pktCtrl != null) { 3298- try { 3299- pktCtrl.setPacketSize(getPacketSize(tracks[i].getFormat(), packetRate)); 3300- } 3301- catch (IllegalArgumentException e) { 3302- pktCtrl.setPacketSize(80); 3303- // Do nothing 3304- } 3305- } 3306- 3307- if (tracks[i].getFormat().getEncoding().equals(AudioFormat.ULAW_RTP)) { 3308- Codec codec[] = new Codec[3]; 3309- 3310- codec[0] = new com.ibm.media.codec.audio.rc.RCModule(); 3311- codec[1] = new com.ibm.media.codec.audio.ulaw.JavaEncoder(); 3312- codec[2] = new com.sun.media.codec.audio.ulaw.Packetizer(); 3313- ((com.sun.media.codec.audio.ulaw.Packetizer) codec 3314- [2]).setPacketSize(160); 3315- 3316- try { 3317- tracks[i].setCodecChain(codec); 3318- } 3319- catch (UnsupportedPlugInException e) { 3320- e.printStackTrace(); 3321- } 3322- } 3323- 3324- } 3325- 3326- atLeastOneTrack = true; 3327- } 3328- else 3329- tracks[i].setEnabled(false); 3330- } 3331- else 3332- tracks[i].setEnabled(false); 3333- } 3334- } 3335- 3336- if (!atLeastOneTrack) 3337- return "Couldn't set any of the tracks to a valid RTP format"; 3338- 3339- result = waitForState(processor, Controller.Realized); 3340- if (!result) 3341- return "Couldn't realize processor"; 3342- 3343- // Get the output data source of the processor 3344- dataOutput = processor.getDataOutput(); 3345- 3346- return null; 3347- } 3348- 3349- /** 3350- * Get the best packet size for a given codec and a codec rate 3351- * 3352- * @param codecFormat 3353- * @param milliseconds 3354- * @return 3355- * @throws IllegalArgumentException 3356- */ 3357- private int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException { 3358- String encoding = codecFormat.getEncoding(); 3359- if (encoding.equalsIgnoreCase(AudioFormat.GSM) || 3360- encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) { 3361- return milliseconds * 4; // 1 byte per millisec 3362- } 3363- else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) || 3364- encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) { 3365- return milliseconds * 8; 3366- } 3367- else { 3368- throw new IllegalArgumentException("Unknown codec type"); 3369- } 3370- } 3371- 3372- /** 3373- * Use the RTPManager API to create sessions for each jmf 3374- * track of the processor. 3375- * 3376- * @return description 3377- */ 3378- private String createTransmitter() { 3379- 3380- // Cheated. Should have checked the type. 3381- PushBufferDataSource pbds = (PushBufferDataSource) dataOutput; 3382- PushBufferStream pbss[] = pbds.getStreams(); 3383- 3384- rtpMgrs = new RTPManager[pbss.length]; 3385- SessionAddress localAddr, destAddr; 3386- InetAddress ipAddr; 3387- SendStream sendStream; 3388- audioReceiver = new AudioReceiver(this, jingleMediaSession); 3389- int port; 3390- 3391- for (int i = 0; i < pbss.length; i++) { 3392- try { 3393- rtpMgrs[i] = RTPManager.newInstance(); 3394- 3395- port = portBase + 2 * i; 3396- ipAddr = InetAddress.getByName(remoteIpAddress); 3397- 3398- localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress), 3399- localPort); 3400- 3401- destAddr = new SessionAddress(ipAddr, port); 3402- 3403- rtpMgrs[i].addReceiveStreamListener(audioReceiver); 3404- rtpMgrs[i].addSessionListener(audioReceiver); 3405- 3406- BufferControl bc = (BufferControl) rtpMgrs[i].getControl("javax.media.control.BufferControl"); 3407- if (bc != null) { 3408- int bl = 160; 3409- bc.setBufferLength(bl); 3410- } 3411- 3412- try { 3413- 3414- rtpMgrs[i].initialize(localAddr); 3415- 3416- } 3417- catch (InvalidSessionAddressException e) { 3418- // In case the local address is not allowed to read, we user another local address 3419- SessionAddress sessAddr = new SessionAddress(); 3420- localAddr = new SessionAddress(sessAddr.getDataAddress(), 3421- localPort); 3422- rtpMgrs[i].initialize(localAddr); 3423- } 3424- 3425- rtpMgrs[i].addTarget(destAddr); 3426- 3427- LOGGER.error("Created RTP session at " + localPort + " to: " + remoteIpAddress + " " + port); 3428- 3429- sendStream = rtpMgrs[i].createSendStream(dataOutput, i); 3430- 3431- sendStreams.add(sendStream); 3432- 3433- sendStream.start(); 3434- 3435- } 3436- catch (Exception e) { 3437- e.printStackTrace(); 3438- return e.getMessage(); 3439- } 3440- } 3441- 3442- return null; 3443- } 3444- 3445- /** 3446- * Set transmit activity. If the active is true, the instance should trasmit. 3447- * If it is set to false, the instance should pause transmit. 3448- * 3449- * @param active active state 3450- */ 3451- public void setTrasmit(boolean active) { 3452- for (SendStream sendStream : sendStreams) { 3453- try { 3454- if (active) { 3455- sendStream.start(); 3456- LOGGER.debug("START"); 3457- } 3458- else { 3459- sendStream.stop(); 3460- LOGGER.debug("STOP"); 3461- } 3462- } 3463- catch (IOException e) { 3464- e.printStackTrace(); 3465- } 3466- 3467- } 3468- } 3469- 3470- /** 3471- * ************************************************************* 3472- * Convenience methods to handle processor's state changes. 3473- * ************************************************************** 3474- */ 3475- 3476- private Integer stateLock = 0; 3477- private boolean failed = false; 3478- 3479- Integer getStateLock() { 3480- return stateLock; 3481- } 3482- 3483- void setFailed() { 3484- failed = true; 3485- } 3486- 3487- private synchronized boolean waitForState(Processor p, int state) { 3488- p.addControllerListener(new StateListener()); 3489- failed = false; 3490- 3491- // Call the required method on the processor 3492- if (state == Processor.Configured) { 3493- p.configure(); 3494- } 3495- else if (state == Processor.Realized) { 3496- p.realize(); 3497- } 3498- 3499- // Wait until we get an event that confirms the 3500- // success of the method, or a failure event. 3501- // See StateListener inner class 3502- while (p.getState() < state && !failed) { 3503- synchronized (getStateLock()) { 3504- try { 3505- getStateLock().wait(); 3506- } 3507- catch (InterruptedException ie) { 3508- return false; 3509- } 3510- } 3511- } 3512- 3513- return !failed; 3514- } 3515- 3516- /** 3517- * ************************************************************* 3518- * Inner Classes 3519- * ************************************************************** 3520- */ 3521- 3522- class StateListener implements ControllerListener { 3523- 3524- public void controllerUpdate(ControllerEvent ce) { 3525- 3526- // If there was an error during configure or 3527- // realize, the processor will be closed 3528- if (ce instanceof ControllerClosedEvent) 3529- setFailed(); 3530- 3531- // All controller events, send a notification 3532- // to the waiting thread in waitForState method. 3533- if (ce != null) { 3534- synchronized (getStateLock()) { 3535- getStateLock().notifyAll(); 3536- } 3537- } 3538- } 3539- } 3540- 3541- public static void main(String args[]) { 3542- 3543- InetAddress localhost; 3544- try { 3545- localhost = InetAddress.getLocalHost(); 3546- 3547- AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP), null); 3548- AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP), null); 3549- 3550- audioChannel0.start(); 3551- audioChannel1.start(); 3552- 3553- try { 3554- Thread.sleep(5000); 3555- } 3556- catch (InterruptedException e) { 3557- e.printStackTrace(); 3558- } 3559- 3560- audioChannel0.setTrasmit(false); 3561- audioChannel1.setTrasmit(false); 3562- 3563- try { 3564- Thread.sleep(5000); 3565- } 3566- catch (InterruptedException e) { 3567- e.printStackTrace(); 3568- } 3569- 3570- audioChannel0.setTrasmit(true); 3571- audioChannel1.setTrasmit(true); 3572- 3573- try { 3574- Thread.sleep(5000); 3575- } 3576- catch (InterruptedException e) { 3577- e.printStackTrace(); 3578- } 3579- 3580- audioChannel0.stop(); 3581- audioChannel1.stop(); 3582- 3583- } 3584- catch (UnknownHostException e) { 3585- e.printStackTrace(); 3586- } 3587- 3588- } 3589-} 3590\ No newline at end of file 3591Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java 3592=================================================================== 3593--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java (revision 11644) 3594+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java (working copy) 3595@@ -1,55 +0,0 @@ 3596-/** 3597- * $RCSfile: AudioFormatUtils.java,v $ 3598- * $Revision: 1.1 $ 3599- * $Date: 08/11/2006 3600- * <p/> 3601- * Copyright 2003-2006 Jive Software. 3602- * <p/> 3603- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 3604- * you may not use this file except in compliance with the License. 3605- * You may obtain a copy of the License at 3606- * <p/> 3607- * http://www.apache.org/licenses/LICENSE-2.0 3608- * <p/> 3609- * Unless required by applicable law or agreed to in writing, software 3610- * distributed under the License is distributed on an "AS IS" BASIS, 3611- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3612- * See the License for the specific language governing permissions and 3613- * limitations under the License. 3614- */ 3615-package org.jivesoftware.smackx.jingle.mediaimpl.jmf; 3616- 3617-import org.jivesoftware.smackx.jingle.media.PayloadType; 3618- 3619-import javax.media.format.AudioFormat; 3620- 3621-/** 3622- * Audio Format Utils. 3623- * 3624- * @author Thiago Camargo 3625- */ 3626-public class AudioFormatUtils { 3627- 3628- /** 3629- * Return a JMF AudioFormat for a given Jingle Payload type. 3630- * Return null if the payload is not supported by this jmf API. 3631- * 3632- * @param payloadtype payloadtype 3633- * @return correspondent audioType 3634- */ 3635- public static AudioFormat getAudioFormat(PayloadType payloadtype) { 3636- 3637- switch (payloadtype.getId()) { 3638- case 0: 3639- return new AudioFormat(AudioFormat.ULAW_RTP); 3640- case 3: 3641- return new AudioFormat(AudioFormat.GSM_RTP); 3642- case 4: 3643- return new AudioFormat(AudioFormat.G723_RTP); 3644- default: 3645- return null; 3646- } 3647- 3648- } 3649- 3650-} 3651Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java 3652=================================================================== 3653--- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java (revision 11644) 3654+++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java (working copy) 3655@@ -1,134 +0,0 @@ 3656-/** 3657- * $RCSfile: SpeexMediaManager.java,v $ 3658- * $Revision: 1.3 $ 3659- * $Date: 25/12/2006 3660- * <p/> 3661- * Copyright 2003-2006 Jive Software. 3662- * <p/> 3663- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 3664- * you may not use this file except in compliance with the License. 3665- * You may obtain a copy of the License at 3666- * <p/> 3667- * http://www.apache.org/licenses/LICENSE-2.0 3668- * <p/> 3669- * Unless required by applicable law or agreed to in writing, software 3670- * distributed under the License is distributed on an "AS IS" BASIS, 3671- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3672- * See the License for the specific language governing permissions and 3673- * limitations under the License. 3674- */ 3675-package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; 3676- 3677-import java.io.File; 3678-import java.io.IOException; 3679-import java.util.ArrayList; 3680-import java.util.List; 3681- 3682-import org.jivesoftware.smackx.jingle.JingleSession; 3683-import org.jivesoftware.smackx.jingle.SmackLogger; 3684-import org.jivesoftware.smackx.jingle.media.JingleMediaManager; 3685-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 3686-import org.jivesoftware.smackx.jingle.media.PayloadType; 3687-import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; 3688-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; 3689-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 3690- 3691-/** 3692- * Implements a jingleMediaManager using JMF based API and JSpeex. 3693- * It supports Speex codec. 3694- * <i>This API only currently works on windows.</i> 3695- * 3696- * @author Thiago Camargo 3697- */ 3698-public class SpeexMediaManager extends JingleMediaManager { 3699- 3700- private static final SmackLogger LOGGER = SmackLogger.getLogger(SpeexMediaManager.class); 3701- 3702- public static final String MEDIA_NAME = "Speex"; 3703- 3704- private List<PayloadType> payloads = new ArrayList<PayloadType>(); 3705- 3706- public SpeexMediaManager(JingleTransportManager transportManager) { 3707- super(transportManager); 3708- setupPayloads(); 3709- setupJMF(); 3710- } 3711- 3712- /** 3713- * Returns a new jingleMediaSession 3714- * 3715- * @param payloadType payloadType 3716- * @param remote remote Candidate 3717- * @param local local Candidate 3718- * @return JingleMediaSession 3719- */ 3720- public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { 3721- return new AudioMediaSession(payloadType, remote, local, null,null); 3722- } 3723- 3724- /** 3725- * Setup API supported Payloads 3726- */ 3727- private void setupPayloads() { 3728- payloads.add(new PayloadType.Audio(15, "speex")); 3729- } 3730- 3731- /** 3732- * Return all supported Payloads for this Manager 3733- * 3734- * @return The Payload List 3735- */ 3736- public List<PayloadType> getPayloads() { 3737- return payloads; 3738- } 3739- 3740- /** 3741- * Runs JMFInit the first time the application is started so that capture 3742- * devices are properly detected and initialized by JMF. 3743- */ 3744- public static void setupJMF() { 3745- // .jmf is the place where we store the jmf.properties file used 3746- // by JMF. if the directory does not exist or it does not contain 3747- // a jmf.properties file. or if the jmf.properties file has 0 length 3748- // then this is the first time we're running and should continue to 3749- // with JMFInit 3750- String homeDir = System.getProperty("user.home"); 3751- File jmfDir = new File(homeDir, ".jmf"); 3752- String classpath = System.getProperty("java.class.path"); 3753- classpath += System.getProperty("path.separator") 3754- + jmfDir.getAbsolutePath(); 3755- System.setProperty("java.class.path", classpath); 3756- 3757- if (!jmfDir.exists()) 3758- jmfDir.mkdir(); 3759- 3760- File jmfProperties = new File(jmfDir, "jmf.properties"); 3761- 3762- if (!jmfProperties.exists()) { 3763- try { 3764- jmfProperties.createNewFile(); 3765- } 3766- catch (IOException ex) { 3767- LOGGER.debug("Failed to create jmf.properties"); 3768- ex.printStackTrace(); 3769- } 3770- } 3771- 3772- // if we're running on linux checkout that libjmutil.so is where it 3773- // should be and put it there. 3774- runLinuxPreInstall(); 3775- 3776- if (jmfProperties.length() == 0) { 3777- new JMFInit(null, false); 3778- } 3779- 3780- } 3781- 3782- private static void runLinuxPreInstall() { 3783- // @TODO Implement Linux Pre-Install 3784- } 3785- 3786- public String getName() { 3787- return MEDIA_NAME; 3788- } 3789-} 3790Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java 3791=================================================================== 3792--- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java (revision 11644) 3793+++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java (working copy) 3794@@ -1,245 +0,0 @@ 3795-/** 3796- * $RCSfile: AudioMediaSession.java,v $ 3797- * $Revision: 1.1 $ 3798- * $Date: 25/12/2006 3799- * <p/> 3800- * Copyright 2003-2006 Jive Software. 3801- * <p/> 3802- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 3803- * you may not use this file except in compliance with the License. 3804- * You may obtain a copy of the License at 3805- * <p/> 3806- * http://www.apache.org/licenses/LICENSE-2.0 3807- * <p/> 3808- * Unless required by applicable law or agreed to in writing, software 3809- * distributed under the License is distributed on an "AS IS" BASIS, 3810- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3811- * See the License for the specific language governing permissions and 3812- * limitations under the License. 3813- */ 3814- 3815-package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; 3816- 3817-import java.io.IOException; 3818-import java.net.DatagramSocket; 3819-import java.net.InetAddress; 3820-import java.net.ServerSocket; 3821-import java.security.GeneralSecurityException; 3822- 3823-import javax.media.NoProcessorException; 3824-import javax.media.format.UnsupportedFormatException; 3825-import javax.media.rtp.rtcp.SenderReport; 3826-import javax.media.rtp.rtcp.SourceDescription; 3827- 3828-import mil.jfcom.cie.media.session.MediaSession; 3829-import mil.jfcom.cie.media.session.MediaSessionListener; 3830-import mil.jfcom.cie.media.session.StreamPlayer; 3831-import mil.jfcom.cie.media.srtp.packetizer.SpeexFormat; 3832- 3833-import org.jivesoftware.smackx.jingle.JingleSession; 3834-import org.jivesoftware.smackx.jingle.SmackLogger; 3835-import org.jivesoftware.smackx.jingle.media.JingleMediaSession; 3836-import org.jivesoftware.smackx.jingle.media.PayloadType; 3837-import org.jivesoftware.smackx.jingle.nat.TransportCandidate; 3838- 3839-/** 3840- * This Class implements a complete JingleMediaSession. 3841- * It sould be used to transmit and receive audio captured from the Mic. 3842- * This Class should be automaticly controlled by JingleSession. 3843- * But you could also use in any VOIP application. 3844- * For better NAT Traversal support this implementation don't support only receive or only transmit. 3845- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() 3846- * 3847- * @author Thiago Camargo 3848- */ 3849- 3850-public class AudioMediaSession extends JingleMediaSession implements MediaSessionListener { 3851- 3852- private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); 3853- 3854- private MediaSession mediaSession; 3855- 3856- /** 3857- * Create a Session using Speex Codec 3858- * 3859- * @param localhost localHost 3860- * @param localPort localPort 3861- * @param remoteHost remoteHost 3862- * @param remotePort remotePort 3863- * @param eventHandler eventHandler 3864- * @param quality quality 3865- * @param secure secure 3866- * @param micOn micOn 3867- * @return MediaSession 3868- * @throws NoProcessorException 3869- * @throws UnsupportedFormatException 3870- * @throws IOException 3871- * @throws GeneralSecurityException 3872- */ 3873- 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 { 3874- 3875- SpeexFormat.setFramesPerPacket(1); 3876- /** 3877- * The master key. Hardcoded for now. 3878- */ 3879- 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}; 3880- 3881- /** 3882- * The master salt. Hardcoded for now. 3883- */ 3884- 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}; 3885- 3886- DatagramSocket[] localPorts = MediaSession.getLocalPorts(InetAddress.getByName(localhost), localPort); 3887- MediaSession session = MediaSession.createInstance(remoteHost, remotePort, localPorts, quality, secure, masterKey, masterSalt); 3888- session.setListener(eventHandler); 3889- 3890- 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)}); 3891- return session; 3892- } 3893- 3894- 3895- /** 3896- * Creates a org.jivesoftware.jingleaudio.jspeex.AudioMediaSession with defined payload type, remote and local candidates 3897- * 3898- * @param payloadType Payload of the jmf 3899- * @param remote the remote information. The candidate that the jmf will be sent to. 3900- * @param local the local information. The candidate that will receive the jmf 3901- * @param locator media locator 3902- */ 3903- public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, 3904- final TransportCandidate local, String locator, JingleSession jingleSession) { 3905- super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession); 3906- initialize(); 3907- } 3908- 3909- /** 3910- * Initialize the Audio Channel to make it able to send and receive audio 3911- */ 3912- public void initialize() { 3913- 3914- String ip; 3915- String localIp; 3916- int localPort; 3917- int remotePort; 3918- 3919- if (this.getLocal().getSymmetric() != null) { 3920- ip = this.getLocal().getIp(); 3921- localIp = this.getLocal().getLocalIp(); 3922- localPort = getFreePort(); 3923- remotePort = this.getLocal().getSymmetric().getPort(); 3924- 3925- LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); 3926- 3927- } 3928- else { 3929- ip = this.getRemote().getIp(); 3930- localIp = this.getLocal().getLocalIp(); 3931- localPort = this.getLocal().getPort(); 3932- remotePort = this.getRemote().getPort(); 3933- } 3934- 3935- try { 3936- mediaSession = createSession(localIp, localPort, ip, remotePort, this, 2, false, true); 3937- } 3938- catch (NoProcessorException e) { 3939- e.printStackTrace(); 3940- } 3941- catch (UnsupportedFormatException e) { 3942- e.printStackTrace(); 3943- } 3944- catch (IOException e) { 3945- e.printStackTrace(); 3946- } 3947- catch (GeneralSecurityException e) { 3948- e.printStackTrace(); 3949- } 3950- } 3951- 3952- /** 3953- * Starts transmission and for NAT Traversal reasons start receiving also. 3954- */ 3955- public void startTrasmit() { 3956- try { 3957- LOGGER.debug("start"); 3958- mediaSession.start(true); 3959- this.mediaReceived(""); 3960- } 3961- catch (IOException e) { 3962- e.printStackTrace(); 3963- } 3964- } 3965- 3966- /** 3967- * Set transmit activity. If the active is true, the instance should trasmit. 3968- * If it is set to false, the instance should pause transmit. 3969- * 3970- * @param active active state 3971- */ 3972- public void setTrasmit(boolean active) { 3973- // Do nothing 3974- } 3975- 3976- /** 3977- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 3978- */ 3979- public void startReceive() { 3980- // Do nothing 3981- } 3982- 3983- /** 3984- * Stops transmission and for NAT Traversal reasons stop receiving also. 3985- */ 3986- public void stopTrasmit() { 3987- if (mediaSession != null) 3988- mediaSession.close(); 3989- } 3990- 3991- /** 3992- * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 3993- */ 3994- public void stopReceive() { 3995- // Do nothing 3996- } 3997- 3998- public void newStreamIdentified(StreamPlayer streamPlayer) { 3999- } 4000- 4001- public void senderReportReceived(SenderReport report) { 4002- } 4003- 4004- public void streamClosed(StreamPlayer stream, boolean timeout) { 4005- } 4006- 4007- /** 4008- * Obtain a free port we can use. 4009- * 4010- * @return A free port number. 4011- */ 4012- protected int getFreePort() { 4013- ServerSocket ss; 4014- int freePort = 0; 4015- 4016- for (int i = 0; i < 10; i++) { 4017- freePort = (int) (10000 + Math.round(Math.random() * 10000)); 4018- freePort = freePort % 2 == 0 ? freePort : freePort + 1; 4019- try { 4020- ss = new ServerSocket(freePort); 4021- freePort = ss.getLocalPort(); 4022- ss.close(); 4023- return freePort; 4024- } 4025- catch (IOException e) { 4026- e.printStackTrace(); 4027- } 4028- } 4029- try { 4030- ss = new ServerSocket(0); 4031- freePort = ss.getLocalPort(); 4032- ss.close(); 4033- } 4034- catch (IOException e) { 4035- e.printStackTrace(); 4036- } 4037- return freePort; 4038- } 4039-} 4040