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