001/** 002 * 003 * Copyright the original author or authors 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.jingle.mediaimpl.sshare.api; 018 019import java.awt.AWTException; 020import java.awt.Rectangle; 021import java.awt.Robot; 022import java.awt.image.BufferedImage; 023import java.awt.image.PixelGrabber; 024import java.io.ByteArrayOutputStream; 025import java.io.IOException; 026import java.net.DatagramPacket; 027import java.net.DatagramSocket; 028import java.net.InetAddress; 029import java.util.Arrays; 030import java.util.logging.Logger; 031 032/** 033 * UDP Image Receiver. 034 * It uses PNG Tiles into UDP packets. 035 * 036 * @author Thiago Rocha Camargo 037 */ 038public class ImageTransmitter implements Runnable { 039 040 private static final Logger LOGGER = Logger.getLogger(ImageTransmitter.class.getName()); 041 042 private Robot robot; 043 private InetAddress localHost; 044 private InetAddress remoteHost; 045 private int localPort; 046 private int remotePort; 047 public static final int tileWidth = 25; 048 private boolean on = true; 049 private boolean transmit = false; 050 private DatagramSocket socket; 051 private Rectangle area; 052 private int tiles[][][]; 053 private int maxI; 054 private int maxJ; 055 private ImageEncoder encoder; 056 public final static int KEYFRAME = 10; 057 058 public ImageTransmitter(DatagramSocket socket, InetAddress remoteHost, int remotePort, Rectangle area) { 059 060 try { 061 robot = new Robot(); 062 063 maxI = (int) Math.ceil(area.getWidth() / tileWidth); 064 maxJ = (int) Math.ceil(area.getHeight() / tileWidth); 065 066 tiles = new int[maxI][maxJ][tileWidth * tileWidth]; 067 068 this.area = area; 069 this.socket = socket; 070 localHost = socket.getLocalAddress(); 071 localPort = socket.getLocalPort(); 072 this.remoteHost = remoteHost; 073 this.remotePort = remotePort; 074 this.encoder = new DefaultEncoder(); 075 076 transmit = true; 077 078 } 079 catch (AWTException e) { 080 e.printStackTrace(); 081 } 082 083 } 084 085 public void start() { 086 byte buf[] = new byte[1024]; 087 final DatagramPacket p = new DatagramPacket(buf, 1024); 088 089 int keyframe = 0; 090 091 while (on) { 092 if (transmit) { 093 094 BufferedImage capture = robot.createScreenCapture(area); 095 096 QuantizeFilter filter = new QuantizeFilter(); 097 capture = filter.filter(capture, null); 098 099 long trace = System.currentTimeMillis(); 100 101 if (++keyframe > KEYFRAME) { 102 keyframe = 0; 103 } 104 LOGGER.fine("KEYFRAME:" + keyframe); 105 106 for (int i = 0; i < maxI; i++) { 107 for (int j = 0; j < maxJ; j++) { 108 109 final BufferedImage bufferedImage = capture.getSubimage(i * tileWidth, j * tileWidth, tileWidth, tileWidth); 110 111 int pixels[] = new int[tileWidth * tileWidth]; 112 113 PixelGrabber pg = new PixelGrabber(bufferedImage, 0, 0, tileWidth, tileWidth, pixels, 0, tileWidth); 114 115 try { 116 if (pg.grabPixels()) { 117 118 if (keyframe == KEYFRAME || !Arrays.equals(tiles[i][j], pixels)) { 119 120 ByteArrayOutputStream baos = encoder.encode(bufferedImage); 121 122 if (baos != null) { 123 124 try { 125 126 Thread.sleep(1); 127 128 baos.write(i); 129 baos.write(j); 130 131 byte[] bytesOut = baos.toByteArray(); 132 133 if (bytesOut.length > 1000) 134 LOGGER.severe("Bytes out > 1000. Equals " + bytesOut.length); 135 136 p.setData(bytesOut); 137 p.setAddress(remoteHost); 138 p.setPort(remotePort); 139 140 try { 141 socket.send(p); 142 } 143 catch (IOException e) { 144 e.printStackTrace(); 145 } 146 147 tiles[i][j] = pixels; 148 149 } 150 catch (Exception e) { 151 } 152 153 } 154 155 } 156 157 } 158 } 159 catch (InterruptedException e) { 160 e.printStackTrace(); 161 } 162 } 163 } 164 165 trace = (System.currentTimeMillis() - trace); 166 LOGGER.fine("Loop Time:" + trace); 167 168 if (trace < 500) { 169 try { 170 Thread.sleep(500 - trace); 171 } 172 catch (InterruptedException e) { 173 e.printStackTrace(); 174 } 175 } 176 } 177 } 178 } 179 180 public void run() { 181 start(); 182 } 183 184 /** 185 * Set Transmit Enabled/Disabled 186 * 187 * @param transmit boolean Enabled/Disabled 188 */ 189 public void setTransmit(boolean transmit) { 190 this.transmit = transmit; 191 } 192 193 /** 194 * Get the encoder used to encode Images Tiles 195 * 196 * @return encoder 197 */ 198 public ImageEncoder getEncoder() { 199 return encoder; 200 } 201 202 /** 203 * Set the encoder used to encode Image Tiles 204 * 205 * @param encoder encoder 206 */ 207 public void setEncoder(ImageEncoder encoder) { 208 this.encoder = encoder; 209 } 210 211 /** 212 * Stops Transmitter 213 */ 214 public void stop() { 215 this.transmit = false; 216 this.on = false; 217 socket.close(); 218 } 219}