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}