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}