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 * @param jingleSession the jingle session. 068 */ 069 @SuppressWarnings("this-escape") 070 public ScreenShareSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, 071 final String locator, JingleSession jingleSession) { 072 super(payloadType, remote, local, "Screen", jingleSession); 073 initialize(); 074 } 075 076 /** 077 * Initialize the screen share channels. 078 */ 079 @Override 080 public void initialize() { 081 082 JingleSession session = getJingleSession(); 083 if (session != null && session.getInitiator().equals(session.getConnection().getUser())) { 084 // If the initiator of the jingle session is us then we transmit a screen share. 085 try { 086 InetAddress remote = InetAddress.getByName(getRemote().getIp()); 087 transmitter = new ImageTransmitter(new DatagramSocket(getLocal().getPort()), remote, getRemote().getPort(), 088 new Rectangle(0, 0, width, height)); 089 } catch (Exception e) { 090 LOGGER.log(Level.WARNING, "exception", e); 091 } 092 093 } else { 094 // Otherwise we receive a screen share. 095 JFrame window = new JFrame(); 096 JPanel jp = new JPanel(); 097 window.add(jp); 098 099 window.setLocation(0, 0); 100 window.setSize(600, 600); 101 102 window.addWindowListener(new WindowAdapter() { 103 @Override 104 public void windowClosed(WindowEvent e) { 105 receiver.stop(); 106 } 107 }); 108 109 try { 110 receiver = new ImageReceiver(InetAddress.getByName("0.0.0.0"), getRemote().getPort(), getLocal().getPort(), width, 111 height); 112 LOGGER.fine("Receiving on:" + receiver.getLocalPort()); 113 } catch (UnknownHostException e) { 114 LOGGER.log(Level.WARNING, "exception", e); 115 } 116 117 jp.add(receiver); 118 receiver.setVisible(true); 119 window.setAlwaysOnTop(true); 120 window.setVisible(true); 121 } 122 } 123 124 /** 125 * Starts transmission and for NAT Traversal reasons start receiving also. 126 * 127 * @deprecated use {@link #startTransmit()} instead. 128 */ 129 @Deprecated 130 public void startTrasmit() { 131 startTransmit(); 132 } 133 134 /** 135 * Starts transmission and for NAT Traversal reasons start receiving also. 136 */ 137 @Override 138 public void startTransmit() { 139 new Thread(transmitter).start(); 140 } 141 142 /** 143 * Set transmit activity. If the active is true, the instance should transmit. 144 * If it is set to false, the instance should pause transmit. 145 * 146 * @param active active state 147 * @deprecated use {@link #setTransmit(boolean)} instead. 148 */ 149 @Deprecated 150 public void setTrasmit(boolean active) { 151 setTransmit(active); 152 } 153 154 /** 155 * Set transmit activity. If the active is true, the instance should transmit. 156 * If it is set to false, the instance should pause transmit. 157 * 158 * @param active active state 159 */ 160 @Override 161 public void setTransmit(boolean active) { 162 transmitter.setTransmit(true); 163 } 164 165 /** 166 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 167 */ 168 @Override 169 public void startReceive() { 170 // Do nothing 171 } 172 173 /** 174 * Stops transmission and for NAT Traversal reasons stop receiving also. 175 * 176 * @deprecated use {@link #stopTransmit()} instead. 177 */ 178 @Deprecated 179 public void stopTrasmit() { 180 stopTransmit(); 181 } 182 183 /** 184 * Stops transmission and for NAT Traversal reasons stop receiving also. 185 */ 186 @Override 187 public void stopTransmit() { 188 if (transmitter != null) { 189 transmitter.stop(); 190 } 191 } 192 193 /** 194 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 195 */ 196 @Override 197 public void stopReceive() { 198 if (receiver != null) { 199 receiver.stop(); 200 } 201 } 202 203 /** 204 * Obtain a free port we can use. 205 * 206 * @return A free port number. 207 */ 208 protected int getFreePort() { 209 ServerSocket ss; 210 int freePort = 0; 211 212 for (int i = 0; i < 10; i++) { 213 freePort = (int) (10000 + Math.round(Math.random() * 10000)); 214 freePort = freePort % 2 == 0 ? freePort : freePort + 1; 215 try { 216 ss = new ServerSocket(freePort); 217 freePort = ss.getLocalPort(); 218 ss.close(); 219 return freePort; 220 } catch (IOException e) { 221 LOGGER.log(Level.WARNING, "exception", e); 222 } 223 } 224 try { 225 ss = new ServerSocket(0); 226 freePort = ss.getLocalPort(); 227 ss.close(); 228 } catch (IOException e) { 229 LOGGER.log(Level.WARNING, "exception", e); 230 } 231 return freePort; 232 } 233 234 public void setEncoder(ImageEncoder encoder) { 235 if (encoder != null) { 236 this.transmitter.setEncoder(encoder); 237 } 238 } 239 240 public void setDecoder(ImageDecoder decoder) { 241 if (decoder != null) { 242 this.receiver.setDecoder(decoder); 243 } 244 } 245}