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