/** * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ * * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). * * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. * */ package org.bigbluebutton.deskshare.client.blocks; import java.awt.Point; import java.awt.image.BufferedImage; import java.util.concurrent.atomic.AtomicLong; import org.bigbluebutton.deskshare.client.net.EncodedBlockData; import org.bigbluebutton.deskshare.common.PixelExtractException; import org.bigbluebutton.deskshare.common.ScreenVideoEncoder; import org.bigbluebutton.deskshare.common.Dimension; public final class Block { private static final int KEEP_ALIVE_INTERVAL = 30000; private static final int DIRTY_COUNT_LIMIT = 10; private final BlockChecksum checksum; private final Dimension dim; private final int position; private final Point location; private int[] capturedPixels; private final Object pixelsLock = new Object(); private long lastSent = System.currentTimeMillis(); private AtomicLong sentCount = new AtomicLong(); private Integer dirtyCount = DIRTY_COUNT_LIMIT; // initially, blocks are sent immediately private boolean useSVC2; Block(Dimension dim, int position, Point location, boolean useSVC2) { checksum = new BlockChecksum(); this.dim = dim; this.position = position; this.location = location; this.useSVC2 = useSVC2; } public boolean hasChanged(BufferedImage capturedScreen) { synchronized(pixelsLock) { try { capturedPixels = ScreenVideoEncoder.getPixels(capturedScreen, getX(), getY(), getWidth(), getHeight(), useSVC2); } catch (PixelExtractException e) { System.out.println(e.toString()); } } synchronized (dirtyCount) { if ((! checksumSame(capturedPixels)) || sendKeepAliveBlock()) { if (dirtyCount >= DIRTY_COUNT_LIMIT) { dirtyCount = 0; return true; } else { dirtyCount++; return false; } } else { if (dirtyCount > 0) { dirtyCount = 0; return true; } else { return false; } } } } private boolean isKeepAliveBlock() { // Use block 1 as our keepalive block. The keepalive block is our audit so that the server knows // that the applet is still connected to the server. So it there's no change in the desktop, the applet // should still send this keepalive block. return position == 1; } private boolean sendKeepAliveBlock() { long now = System.currentTimeMillis(); if (isKeepAliveBlock() && (now - lastSent > KEEP_ALIVE_INTERVAL)) { // Send keepalive block every 30 seconds. lastSent = now; System.out.println("Sending keep alive block!"); return true; } return false; } public void sent() { sentCount.incrementAndGet(); } public EncodedBlockData encode() { int[] pixelsCopy = new int[capturedPixels.length]; synchronized (pixelsLock) { System.arraycopy(capturedPixels, 0, pixelsCopy, 0, capturedPixels.length); } byte[] encodedBlock = useSVC2 ? ScreenVideoEncoder.encodePixelsSVC2(pixelsCopy, getWidth(), getHeight()) : ScreenVideoEncoder.encodePixels(pixelsCopy, getWidth(), getHeight(), (sentCount.longValue() < 5) /* send grayscale image */); return new EncodedBlockData(position, encodedBlock); } private boolean checksumSame(int[] pixels) { return checksum.isChecksumSame(convertIntPixelsToBytePixels(pixels)); } private byte[] convertIntPixelsToBytePixels(int[] pixels) { byte[] p = new byte[pixels.length * 3]; int position = 0; for (int i = 0; i < pixels.length; i++) { byte red = (byte) ((pixels[i] >> 16) & 0xff); byte green = (byte) ((pixels[i] >> 8) & 0xff); byte blue = (byte) (pixels[i] & 0xff); // Sequence should be BGR p[position++] = blue; p[position++] = green; p[position++] = red; } return p; } public int getWidth() { return new Integer(dim.getWidth()).intValue(); } public int getHeight() { return new Integer(dim.getHeight()).intValue(); } public int getPosition() { return new Integer(position).intValue(); } public int getX() { return new Integer(location.x).intValue(); } public int getY() { return new Integer(location.y).intValue(); } Dimension getDimension() { return new Dimension(dim.getWidth(), dim.getHeight()); } Point getLocation() { return new Point(location.x, location.y); } }