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