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}