001/** 002 * 003 * Copyright 2003-2006 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.jingleold.mediaimpl.sshare; 018 019import java.awt.Rectangle; 020import java.awt.event.WindowAdapter; 021import java.awt.event.WindowEvent; 022import java.io.IOException; 023import java.net.DatagramSocket; 024import java.net.InetAddress; 025import java.net.ServerSocket; 026import java.net.UnknownHostException; 027import java.util.logging.Level; 028import java.util.logging.Logger; 029 030import javax.swing.JFrame; 031import javax.swing.JPanel; 032 033import org.jivesoftware.smackx.jingleold.JingleSession; 034import org.jivesoftware.smackx.jingleold.media.JingleMediaSession; 035import org.jivesoftware.smackx.jingleold.media.PayloadType; 036import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageDecoder; 037import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageEncoder; 038import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageReceiver; 039import org.jivesoftware.smackx.jingleold.mediaimpl.sshare.api.ImageTransmitter; 040import org.jivesoftware.smackx.jingleold.nat.TransportCandidate; 041 042/** 043 * This Class implements a complete JingleMediaSession. 044 * It should be used to transmit and receive captured images from the Display. 045 * This Class should be automatically controlled by JingleSession. 046 * For better NAT Traversal support this implementation don't support only receive or only transmit. 047 * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() 048 * 049 * @author Thiago Camargo 050 */ 051public class ScreenShareSession extends JingleMediaSession { 052 053 private static final Logger LOGGER = Logger.getLogger(ScreenShareSession.class.getName()); 054 055 private ImageTransmitter transmitter = null; 056 private ImageReceiver receiver = null; 057 private int width = 600; 058 private int height = 600; 059 060 /** 061 * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates. 062 * 063 * @param payloadType Payload of the jmf 064 * @param remote the remote information. The candidate that the jmf will be sent to. 065 * @param local the local information. The candidate that will receive the jmf 066 * @param locator media locator 067 */ 068 public ScreenShareSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, 069 final String locator, JingleSession jingleSession) { 070 super(payloadType, remote, local, "Screen", jingleSession); 071 initialize(); 072 } 073 074 /** 075 * Initialize the screen share channels. 076 */ 077 @Override 078 public void initialize() { 079 080 JingleSession session = getJingleSession(); 081 if ((session != null) && (session.getInitiator().equals(session.getConnection().getUser()))) { 082 // If the initiator of the jingle session is us then we transmit a screen share. 083 try { 084 InetAddress remote = InetAddress.getByName(getRemote().getIp()); 085 transmitter = new ImageTransmitter(new DatagramSocket(getLocal().getPort()), remote, getRemote().getPort(), 086 new Rectangle(0, 0, width, height)); 087 } catch (Exception e) { 088 LOGGER.log(Level.WARNING, "exception", e); 089 } 090 091 } else { 092 // Otherwise we receive a screen share. 093 JFrame window = new JFrame(); 094 JPanel jp = new JPanel(); 095 window.add(jp); 096 097 window.setLocation(0, 0); 098 window.setSize(600, 600); 099 100 window.addWindowListener(new WindowAdapter() { 101 @Override 102 public void windowClosed(WindowEvent e) { 103 receiver.stop(); 104 } 105 }); 106 107 try { 108 receiver = new ImageReceiver(InetAddress.getByName("0.0.0.0"), getRemote().getPort(), getLocal().getPort(), width, 109 height); 110 LOGGER.fine("Receiving on:" + receiver.getLocalPort()); 111 } catch (UnknownHostException e) { 112 LOGGER.log(Level.WARNING, "exception", e); 113 } 114 115 jp.add(receiver); 116 receiver.setVisible(true); 117 window.setAlwaysOnTop(true); 118 window.setVisible(true); 119 } 120 } 121 122 /** 123 * Starts transmission and for NAT Traversal reasons start receiving also. 124 * 125 * @deprecated use {@link #startTransmit()} instead. 126 */ 127 @Deprecated 128 public void startTrasmit() { 129 startTransmit(); 130 } 131 132 /** 133 * Starts transmission and for NAT Traversal reasons start receiving also. 134 */ 135 @Override 136 public void startTransmit() { 137 new Thread(transmitter).start(); 138 } 139 140 /** 141 * Set transmit activity. If the active is true, the instance should transmit. 142 * If it is set to false, the instance should pause transmit. 143 * 144 * @param active active state 145 * @deprecated use {@link #setTransmit(boolean)} instead. 146 */ 147 @Deprecated 148 public void setTrasmit(boolean active) { 149 setTransmit(active); 150 } 151 152 /** 153 * Set transmit activity. If the active is true, the instance should transmit. 154 * If it is set to false, the instance should pause transmit. 155 * 156 * @param active active state 157 */ 158 @Override 159 public void setTransmit(boolean active) { 160 transmitter.setTransmit(true); 161 } 162 163 /** 164 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 165 */ 166 @Override 167 public void startReceive() { 168 // Do nothing 169 } 170 171 /** 172 * Stops transmission and for NAT Traversal reasons stop receiving also. 173 * 174 * @deprecated use {@link #stopTransmit()} instead. 175 */ 176 @Deprecated 177 public void stopTrasmit() { 178 stopTransmit(); 179 } 180 181 /** 182 * Stops transmission and for NAT Traversal reasons stop receiving also. 183 */ 184 @Override 185 public void stopTransmit() { 186 if (transmitter != null) { 187 transmitter.stop(); 188 } 189 } 190 191 /** 192 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 193 */ 194 @Override 195 public void stopReceive() { 196 if (receiver != null) { 197 receiver.stop(); 198 } 199 } 200 201 /** 202 * Obtain a free port we can use. 203 * 204 * @return A free port number. 205 */ 206 protected int getFreePort() { 207 ServerSocket ss; 208 int freePort = 0; 209 210 for (int i = 0; i < 10; i++) { 211 freePort = (int) (10000 + Math.round(Math.random() * 10000)); 212 freePort = freePort % 2 == 0 ? freePort : freePort + 1; 213 try { 214 ss = new ServerSocket(freePort); 215 freePort = ss.getLocalPort(); 216 ss.close(); 217 return freePort; 218 } catch (IOException e) { 219 LOGGER.log(Level.WARNING, "exception", e); 220 } 221 } 222 try { 223 ss = new ServerSocket(0); 224 freePort = ss.getLocalPort(); 225 ss.close(); 226 } catch (IOException e) { 227 LOGGER.log(Level.WARNING, "exception", e); 228 } 229 return freePort; 230 } 231 232 public void setEncoder(ImageEncoder encoder) { 233 if (encoder != null) { 234 this.transmitter.setEncoder(encoder); 235 } 236 } 237 238 public void setDecoder(ImageDecoder decoder) { 239 if (decoder != null) { 240 this.receiver.setDecoder(decoder); 241 } 242 } 243}