- fix to make applet not consume all of CPU on client

git-svn-id: http://svn.bigbluebutton.org/project-bbb/trunk@78 6ac8b576-4aa1-4e98-a958-8badfeb98c9d
This commit is contained in:
Richard Alam 2010-03-04 22:06:44 +00:00
parent 895ff7d53b
commit fbe76ed23a
14 changed files with 150 additions and 479 deletions

View File

@ -29,12 +29,11 @@ import java.io.ByteArrayOutputStream;
import org.bigbluebutton.deskshare.client.blocks.BlockManager; import org.bigbluebutton.deskshare.client.blocks.BlockManager;
import org.bigbluebutton.deskshare.client.blocks.ChangedBlocksListener; import org.bigbluebutton.deskshare.client.blocks.ChangedBlocksListener;
import org.bigbluebutton.deskshare.client.net.ConnectionException; import org.bigbluebutton.deskshare.client.net.ConnectionException;
import org.bigbluebutton.deskshare.client.net.EncodedBlockData;
import org.bigbluebutton.deskshare.client.net.NetworkStreamSender; import org.bigbluebutton.deskshare.client.net.NetworkStreamSender;
import org.bigbluebutton.deskshare.common.Dimension; import org.bigbluebutton.deskshare.common.Dimension;
import netscape.javascript.*;
public class DeskShareApplet extends Applet implements IScreenCaptureListener, ChangedBlocksListener { public class DeskShareApplet extends Applet implements IScreenCaptureListener, ChangedBlocksListener {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private ScreenCaptureTaker captureTaker; private ScreenCaptureTaker captureTaker;
private ScreenCapture capture; private ScreenCapture capture;
@ -63,9 +62,6 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
y = Integer.parseInt(getParameter("Y")); y = Integer.parseInt(getParameter("Y"));
room = getParameter("ROOM"); room = getParameter("ROOM");
host = getParameter("IP"); host = getParameter("IP");
String t = getParameter("TUNNEL");
System.out.println("Tunnel param " + t);
} }
public void stop() { public void stop() {
@ -76,15 +72,13 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
if (senderStarted) if (senderStarted)
sender.stop(); sender.stop();
} catch (ConnectionException e) { } catch (ConnectionException e) {
// TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
public void start() { public void start() {
System.out.println("RunnerApplet start()"); System.out.println("Deskshare Applet start");
startCapture(); startCapture();
} }
@ -98,7 +92,7 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
blockManager.addListener(this); blockManager.addListener(this);
blockManager.initialize(screenDim, tileDim); blockManager.initialize(screenDim, tileDim);
sender = new NetworkStreamSender(blockManager, host, room); sender = new NetworkStreamSender(host, room, screenDim, tileDim);
connected = sender.connect(); connected = sender.connect();
if (connected) { if (connected) {
captureTaker.addListener(this); captureTaker.addListener(this);
@ -107,52 +101,31 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
captureTakerThread = new Thread(captureTaker, "ScreenCaptureTaker"); captureTakerThread = new Thread(captureTaker, "ScreenCaptureTaker");
captureTakerThread.start(); captureTakerThread.start();
} }
sender.start();
} }
private void testFunctionCall() {
try {
System.out.println("testFunctionCall: test started");
JSObject window = JSObject.getWindow(this);
System.out.println("Calling JavaScript getString();");
if (window == null) System.out.println("WINDOW IS NULL");
else System.out.println("WINDOW IS NOT NULL");
String res = (String) window.eval("getString();");
System.out.println("Got string from JavaScript: \"" + res + "\"");
if (!res.equals("Hello, world!")) {
throw new RuntimeException("string value did not match expected value");
}
Number num = (Number) window.eval("getNumber()");
System.out.println("Got number from JavaScript: " + num);
if (num.intValue() != 5) {
throw new RuntimeException("number value did not match expected value");
}
System.out.println("testFunctionCall: test passed.");
} catch (JSException e) {
e.printStackTrace();
System.out.println("TEST FAILED");
} catch (Exception e2) {
e2.printStackTrace();
System.out.println("TEST FAILED");
}
}
/** /**
* This method is called when the user closes the browser window containing the applet * This method is called when the user closes the browser window containing the applet
* It is very important that the connection to the server is closed at this point. That way the server knows to * It is very important that the connection to the server is closed at this point. That way the server knows to
* close the stream. * close the stream.
*/ */
public void destroy(){ public void destroy() {
try {
sender.stop();
} catch (ConnectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
stop(); stop();
} }
public void setScreenCoordinates(int x, int y){ public void setScreenCoordinates(int x, int y) {
capture.setX(x); capture.setX(x);
capture.setY(y); capture.setY(y);
} }
public void onScreenCaptured(BufferedImage screen, boolean isKeyFrame) { public void onScreenCaptured(BufferedImage screen) {
blockManager.processCapturedScreen(screen, isKeyFrame); blockManager.processCapturedScreen(screen);
} }
@ -161,11 +134,8 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
destroy(); destroy();
} }
public void onChangedTiles(ByteArrayOutputStream pixelData, boolean isKeyFrame) { public void onChangedBlock(EncodedBlockData encodedData) {
if (! senderStarted) { sender.send(encodedData);
sender.start();
senderStarted = true;
}
} }
static public void main (String argv[]) { static public void main (String argv[]) {

View File

@ -25,5 +25,5 @@ import java.awt.image.BufferedImage;
public interface IScreenCaptureListener { public interface IScreenCaptureListener {
public void screenCaptureStopped(); public void screenCaptureStopped();
public void onScreenCaptured(BufferedImage screen, boolean isKeyFrame); public void onScreenCaptured(BufferedImage screen);
} }

View File

@ -42,7 +42,7 @@ public class ScreenCapture {
private Rectangle screenBounds; private Rectangle screenBounds;
private int width, height, x,y, videoWidth, videoHeight; private int width, height, x,y, videoWidth, videoHeight;
public ScreenCapture(int x, int y, int screenWidth, int screenHeight){ public ScreenCapture(int x, int y, int screenWidth, int screenHeight) {
this.width = screenWidth; this.width = screenWidth;
this.height = screenHeight; this.height = screenHeight;
try{ try{
@ -54,26 +54,26 @@ public class ScreenCapture {
this.screenBounds = new Rectangle(x, y, this.width, this.height); this.screenBounds = new Rectangle(x, y, this.width, this.height);
} }
public BufferedImage takeSingleSnapshot(){ public BufferedImage takeSingleSnapshot() {
return robot.createScreenCapture(this.screenBounds); return robot.createScreenCapture(this.screenBounds);
} }
public int getScreenshotWidth(){ public int getScreenshotWidth() {
return toolkit.getScreenSize().width; return toolkit.getScreenSize().width;
} }
public int getScreenshotHeight(){ public int getScreenshotHeight() {
return toolkit.getScreenSize().height; return toolkit.getScreenSize().height;
} }
public void setWidth(int width){ public void setWidth(int width) {
int screenWidth = toolkit.getScreenSize().width; int screenWidth = toolkit.getScreenSize().width;
if (width > screenWidth) this.width = screenWidth; if (width > screenWidth) this.width = screenWidth;
else this.width = width; else this.width = width;
updateBounds(); updateBounds();
} }
public void setHeight(int height){ public void setHeight(int height) {
int screenHeight = toolkit.getScreenSize().height; int screenHeight = toolkit.getScreenSize().height;
if (height > screenHeight) { if (height > screenHeight) {
this.height = screenHeight; this.height = screenHeight;
@ -84,29 +84,29 @@ public class ScreenCapture {
updateBounds(); updateBounds();
} }
public void setX(int x){ public void setX(int x) {
this.x = x; this.x = x;
updateBounds(); updateBounds();
} }
public void setY(int y){ public void setY(int y) {
this.y = y; this.y = y;
updateBounds(); updateBounds();
} }
public void updateBounds(){ public void updateBounds() {
this.screenBounds = new Rectangle(x,y,width,height); this.screenBounds = new Rectangle(x,y,width,height);
} }
public int getWidth(){ public int getWidth() {
return this.width; return this.width;
} }
public int getHeight(){ public int getHeight() {
return this.height; return this.height;
} }
public int getProperFrameRate(){ public int getProperFrameRate() {
long area = width*height; long area = width*height;
if (area > 1000000) return 1; if (area > 1000000) return 1;
else if (area > 600000) return 2; else if (area > 600000) return 2;
@ -115,11 +115,11 @@ public class ScreenCapture {
else return 10; else return 10;
} }
public int getVideoWidth(){ public int getVideoWidth() {
return videoWidth; return videoWidth;
} }
public int getVideoHeight(){ public int getVideoHeight() {
return videoHeight; return videoHeight;
} }

View File

@ -23,11 +23,8 @@ package org.bigbluebutton.deskshare.client;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
public class ScreenCaptureTaker implements Runnable { public class ScreenCaptureTaker implements Runnable {
private ScreenCapture capture; private ScreenCapture capture;
private int timeBase;
private int frameCount = 0;
private IScreenCaptureListener listeners; private IScreenCaptureListener listeners;
private volatile boolean startCapture = false; private volatile boolean startCapture = false;
@ -35,64 +32,34 @@ public class ScreenCaptureTaker implements Runnable {
public ScreenCaptureTaker(ScreenCapture capture){ public ScreenCaptureTaker(ScreenCapture capture){
System.out.println("Capture thread constructor."); System.out.println("Capture thread constructor.");
this.capture = capture; this.capture = capture;
this.timeBase = 1000 / capture.getProperFrameRate();
} }
public void run(){ public void run(){
while (startCapture){ while (startCapture){
System.out.println("----- Taking screen capture -----"); System.out.println("----- Taking screen capture -----");
long snapshotTime = System.currentTimeMillis();
BufferedImage image = capture.takeSingleSnapshot(); BufferedImage image = capture.takeSingleSnapshot();
long snapshotEnd = System.currentTimeMillis();
// System.out.println("Snapshot time = " + (snapshotEnd - snapshotTime) + "ms.");
notifyListeners(image); notifyListeners(image);
long completeTime = System.currentTimeMillis();
// System.out.println("Processing time = " + (completeTime - snapshotTime) + "ms.");
try{ try{
//Thread.sleep(timeBase); System.out.println("Going to sleeeeeep.");
Thread.sleep(500); Thread.sleep(1000);
System.out.println("Just woke up.");
} catch (Exception e){ } catch (Exception e){
System.out.println("Exception sleeping."); System.out.println("Exception sleeping.");
System.exit(0); System.exit(0);
} }
} }
System.out.println("Stopping screen capture."); System.out.println("Stopping screen capture.");
listeners.screenCaptureStopped(); listeners.screenCaptureStopped();
} }
private void notifyListeners(BufferedImage image) { private void notifyListeners(BufferedImage image) {
listeners.onScreenCaptured(image, isKeyFrame()); listeners.onScreenCaptured(image);
} }
private boolean isKeyFrame() {
if (frameCount == 0) {
// System.out.println("Is Key Frame " + frameCount);
frameCount++;
return true;
} else {
// System.out.println("Is Not Key Frame " + frameCount);
if (frameCount < 20) {
frameCount++;
} else {
frameCount = 0;
}
return false;
}
}
public void addListener(IScreenCaptureListener listener) { public void addListener(IScreenCaptureListener listener) {
// listeners.add(listener);
listeners = listener; listeners = listener;
} }
public void removeListener(IScreenCaptureListener listener) {
// listeners.remove(listener);
}
public void setCapture(boolean capture) { public void setCapture(boolean capture) {
startCapture = capture; startCapture = capture;

View File

@ -36,16 +36,9 @@ public final class Block {
private final Adler32 checksum; private final Adler32 checksum;
private final Dimension dim; private final Dimension dim;
private final int position; private final int position;
private final Point location; private final Point location;
private boolean isKeyFrame = false;
private int[] pixels; private int[] pixels;
private EncodedBlockData encodedBlockData; private boolean firstTime = true;
private int nextForceUpdate = 10000;
private final static int MIN_DURATION = 5000;
private final static int MAX_DURATION = 10000;
private long lastChanged;
Block(Dimension dim, int position, Point location) { Block(Dimension dim, int position, Point location) {
checksum = new Adler32(); checksum = new Adler32();
@ -54,10 +47,8 @@ public final class Block {
this.location = location; this.location = location;
} }
public void updateBlock(BufferedImage capturedScreen, boolean isKeyFrame) public void updateBlock(BufferedImage capturedScreen) {
{ synchronized(this) {
synchronized(this) {
this.isKeyFrame = isKeyFrame;
try { try {
pixels = ScreenVideoEncoder.getPixels(capturedScreen, getX(), getY(), getWidth(), getHeight()); pixels = ScreenVideoEncoder.getPixels(capturedScreen, getX(), getY(), getWidth(), getHeight());
} catch (PixelExtractException e) { } catch (PixelExtractException e) {
@ -66,82 +57,44 @@ public final class Block {
} }
} }
public EncodedBlockData encode() { public boolean hasChanged() {
int[] pixelsCopy = new int[pixels.length]; if (firstTime) {
synchronized (this) { firstTime = false;
System.arraycopy(pixels, 0, pixelsCopy, 0, pixels.length); return true;
} }
return ! checksumSame();
byte[] encodedBlock;
boolean hasChanged = false;
/** Seems that this thing isn't working properly.
* The blocks only gets sent after forceUpdate. (ralam Oct 29, 2009)
*/
if (!checksumSame(pixelsCopy)) {
//if (!checksumSame(pixelsCopy) || isKeyFrame) {
encodedBlock = ScreenVideoEncoder.encodePixels(pixelsCopy, getWidth(), getHeight(), false, isKeyFrame);
hasChanged = true;
} else {
encodedBlock = ScreenVideoEncoder.encodeBlockUnchanged();
hasChanged = false;
}
EncodedBlockData data = new EncodedBlockData(position, hasChanged, encodedBlock, isKeyFrame);
return data;
} }
private boolean checksumSame(int[] pixelsCopy) { public EncodedBlockData encode() {
byte[] encodedBlock = ScreenVideoEncoder.encodePixels(pixels, getWidth(), getHeight());
return new EncodedBlockData(position, encodedBlock);
}
private boolean checksumSame() {
long oldsum; long oldsum;
oldsum = checksum.getValue(); oldsum = checksum.getValue();
calcChecksum(pixelsCopy); calcChecksum();
return (oldsum == checksum.getValue()); return (oldsum == checksum.getValue());
} }
private boolean forceUpdate() { private void calcChecksum() {
long now = System.currentTimeMillis();
boolean update = ((now - lastChanged) > nextForceUpdate);
if (update) {
synchronized(this) {
nextUpdate();
lastChanged = now;
}
}
return update;
}
private void nextUpdate() {
//get the range, casting to long to avoid overflow problems
long range = (long)MAX_DURATION - (long)MIN_DURATION + 1;
// compute a fraction of the range, 0 <= frac < range
long fraction = (long)(range * random.nextDouble());
nextForceUpdate = (int)(fraction + 5000);
}
public synchronized EncodedBlockData getEncodedBlockData() {
return encodedBlockData;
}
private synchronized void calcChecksum(int pixelsCopy[])
{
checksum.reset(); checksum.reset();
int height = getHeight(); int height = getHeight();
int width = getWidth(); int width = getWidth();
for (int i = 0; i < height; i++) { for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) { for (int j = 0; j < width; j++) {
if ((i * width + i) % 13 == 0) if ((i * width + j) % 5 == 0)
checksum.update(pixelsCopy[i * width + j]); checksum.update(pixels[i * width + j]);
} }
} }
} }
public int getWidth() public int getWidth() {
{
return new Integer(dim.getWidth()).intValue(); return new Integer(dim.getWidth()).intValue();
} }
public int getHeight() public int getHeight() {
{
return new Integer(dim.getHeight()).intValue(); return new Integer(dim.getHeight()).intValue();
} }

View File

@ -1,135 +0,0 @@
/*
* BigBlueButton - http://www.bigbluebutton.org
*
* Copyright (c) 2008-2009 by respective authors (see below). All rights reserved.
*
* BigBlueButton is free software; you can redistribute it and/or modify it under the
* terms of the GNU Affero General Public License as published by the Free Software
* Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along
* with BigBlueButton; if not, If not, see <http://www.gnu.org/licenses/>.
*
* Author: Richard Alam <ritzalam@gmail.com>
*
* $Id: $x
*/
package org.bigbluebutton.deskshare.client.blocks;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.zip.Adler32;
import org.bigbluebutton.deskshare.common.PixelExtractException;
import org.bigbluebutton.deskshare.common.ScreenVideoEncoder;
import org.bigbluebutton.deskshare.common.Dimension;
public final class BlockImp implements IBlock {
private final Adler32 checksum;
private final Dimension dim;
private final int position;
private final Point location;
private boolean isKeyFrame = false;
private byte[] encodedBlock;
private int[] pixels;
private boolean encoded = false;
BlockImp(Dimension dim, int position, Point location) {
checksum = new Adler32();
this.dim = dim;
this.position = position;
this.location = location;
}
public synchronized void updateBlock(BufferedImage capturedScreen, boolean isKeyFrame)
{
this.isKeyFrame = isKeyFrame;
try {
pixels = ScreenVideoEncoder.getPixels(capturedScreen, getX(), getY(), getWidth(), getHeight());
} catch (PixelExtractException e) {
System.out.println(e.toString());
encodedBlock = ScreenVideoEncoder.encodeBlockUnchanged();
}
}
public synchronized byte[] encode() {
long oldsum;
oldsum = checksum.getValue();
calcChecksum(pixels);
if ((oldsum == checksum.getValue()) && !isKeyFrame) {
encodedBlock = ScreenVideoEncoder.encodeBlockUnchanged();
}
else {
encodedBlock = ScreenVideoEncoder.encodePixels(pixels, getWidth(), getHeight(), false, isKeyFrame);
}
return encodedBlock;
}
public byte[] getEncodedBlock() {
return encodedBlock;
}
private synchronized void calcChecksum(int pixels[])
{
checksum.reset();
int height = getHeight();
int width = getWidth();
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if ((i * width + i) % 13 == 0)
checksum.update(pixels[i * width + j]);
}
}
}
public synchronized boolean isKeyFrame() {
return isKeyFrame;
}
public synchronized boolean hasChanged() {
long oldsum;
oldsum = checksum.getValue();
calcChecksum(pixels);
return ((oldsum == checksum.getValue()) && !isKeyFrame);
}
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);
}
}

View File

@ -23,18 +23,11 @@ package org.bigbluebutton.deskshare.client.blocks;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.bigbluebutton.deskshare.client.encoder.BlockEncodeException;
import org.bigbluebutton.deskshare.client.encoder.ScreenVideoBlockEncoder;
import org.bigbluebutton.deskshare.common.ScreenVideoEncoder;
import org.bigbluebutton.deskshare.common.Dimension; import org.bigbluebutton.deskshare.common.Dimension;
import org.bigbluebutton.deskshare.client.net.EncodedBlockData;
public class BlockManager { public class BlockManager {
private final Map<Integer, Block> blocksMap; private final Map<Integer, Block> blocksMap;
@ -48,12 +41,9 @@ public class BlockManager {
private ByteArrayOutputStream encodedPixelStream = new ByteArrayOutputStream(); private ByteArrayOutputStream encodedPixelStream = new ByteArrayOutputStream();
private Dimension screenDim, blockDim; private Dimension screenDim, blockDim;
// private ScreenVideoBlockEncoder encoder;
public BlockManager() { public BlockManager() {
blocksMap = new HashMap<Integer, Block>(); blocksMap = new HashMap<Integer, Block>();
unmodifiableBlocksMap = Collections.unmodifiableMap(blocksMap); unmodifiableBlocksMap = Collections.unmodifiableMap(blocksMap);
// encoder = new ScreenVideoBlockEncoder();
} }
public void initialize(Dimension screen, Dimension tile) { public void initialize(Dimension screen, Dimension tile) {
@ -72,62 +62,24 @@ public class BlockManager {
} }
} }
public void processCapturedScreen(BufferedImage capturedScreen, boolean isKeyFrame) public void processCapturedScreen(BufferedImage capturedScreen) {
{
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
// Block[] blocks = new Block[numRows * numColumns];
int index = 0;
int numberOfBlocks = numColumns * numRows; int numberOfBlocks = numColumns * numRows;
for (int position = 1; position <= numberOfBlocks; position++) { for (int position = 1; position <= numberOfBlocks; position++) {
Block block = blocksMap.get(new Integer(position)); Block block = blocksMap.get(new Integer(position));
block.updateBlock(capturedScreen, isKeyFrame); block.updateBlock(capturedScreen);
if (block.hasChanged()) {
System.out.println("Block " + block.getPosition() + " has changed.");
// blocks[index++] = block; notifyChangedBlockListener(block.encode());
}
} }
long qEncode = System.currentTimeMillis();
// System.out.println("Grabbing pixels for blocks[" + numberOfBlocks + "] took " + (qEncode-start) + " ms.");
// try {
// encoder.encode(blocks);
// sendEncodedData(isKeyFrame);
// } catch (BlockEncodeException e) {
// e.printStackTrace();
// }
notifyChangedTilesListener(null, isKeyFrame);
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
System.out.println("ProcessCapturedScreen took " + (end-start) + " ms."); System.out.println("ProcessCapturedScreen took " + (end-start) + " ms.");
} }
/*
private void sendEncodedData(boolean isKeyFrame) { private void notifyChangedBlockListener(EncodedBlockData encodedData) {
long start = System.currentTimeMillis(); listeners.onChangedBlock(encodedData);
encodedPixelStream.reset();
byte[] encodedDim = ScreenVideoEncoder.encodeBlockDimensionsAndGridSize(blockDim.getWidth(), screenDim.getWidth(), blockDim.getHeight(), screenDim.getHeight());
byte videoDataHeader = ScreenVideoEncoder.encodeFlvVideoDataHeader(isKeyFrame);
try {
encodedPixelStream.write(videoDataHeader);
encodedPixelStream.write(encodedDim);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int numberOfBlocks = numColumns * numRows;
for (int position = 1; position <= numberOfBlocks; position++) {
Block block = blocksMap.get(new Integer(position));
byte[] data = block.getEncodedBlock();
encodedPixelStream.write(data, 0, data.length);
}
// System.out.println("Encoded data length = " + encodedPixelStream.size());
notifyChangedTilesListener(encodedPixelStream, isKeyFrame);
long end = System.currentTimeMillis();
System.out.println("Sending encoded data took " + (end-start) + " ms.");
}
*/
private void notifyChangedTilesListener(ByteArrayOutputStream encodedPixelStream, boolean isKeyFrame) {
listeners.onChangedTiles(encodedPixelStream, isKeyFrame);
} }
@ -135,9 +87,7 @@ public class BlockManager {
listeners = listener; listeners = listener;
} }
public void removeListener(ChangedBlocksListener listener) { public void removeListener(ChangedBlocksListener listener) {
//listeners.remove(listener);
listeners = null; listeners = null;
} }
@ -145,13 +95,11 @@ public class BlockManager {
return (Block) blocksMap.get(Integer.valueOf(position)); return (Block) blocksMap.get(Integer.valueOf(position));
} }
public int getRowCount() public int getRowCount() {
{
return numRows; return numRows;
} }
public int getColumnCount() public int getColumnCount() {
{
return numColumns; return numColumns;
} }
@ -162,11 +110,4 @@ public class BlockManager {
public Dimension getBlockDim() { public Dimension getBlockDim() {
return blockDim; return blockDim;
} }
/*
* Returns a read-only "live" view of the blocks;
*/
public Map<Integer, Block> getBlocks() {
return unmodifiableBlocksMap;
}
} }

View File

@ -21,9 +21,8 @@
*/ */
package org.bigbluebutton.deskshare.client.blocks; package org.bigbluebutton.deskshare.client.blocks;
import java.io.ByteArrayOutputStream; import org.bigbluebutton.deskshare.client.net.EncodedBlockData;
public interface ChangedBlocksListener { public interface ChangedBlocksListener {
public void onChangedBlock(EncodedBlockData encodedData);
public void onChangedTiles(ByteArrayOutputStream pixelData, boolean isKeyFrame);
} }

View File

@ -22,18 +22,13 @@
package org.bigbluebutton.deskshare.client.net; package org.bigbluebutton.deskshare.client.net;
public class EncodedBlockData { public class EncodedBlockData {
private final byte[] videoData; private final byte[] videoData;
private final boolean keyFrame;
private final int position; private final int position;
private final long timestamp; private final long timestamp;
private final boolean hasChanged;
public EncodedBlockData(int position, boolean hasChanged, byte[] videoData, boolean keyFrame) { public EncodedBlockData(int position, byte[] videoData) {
this.position = position; this.position = position;
this.videoData = videoData; this.videoData = videoData;
this.keyFrame = keyFrame;
this.hasChanged = hasChanged;
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
} }
@ -44,16 +39,8 @@ public class EncodedBlockData {
public byte[] getVideoData() { public byte[] getVideoData() {
return videoData; return videoData;
} }
public boolean isKeyFrame() {
return keyFrame;
}
public long getTimestamp() { public long getTimestamp() {
return timestamp; return timestamp;
} }
public boolean hasChanged() {
return hasChanged;
}
} }

View File

@ -40,12 +40,17 @@ public class NetworkHttpStreamSender implements Runnable {
private URL url; private URL url;
URLConnection conn; URLConnection conn;
private String room; private String room;
private Dimension screenDim;
private Dimension blockDim;
private final NextBlockRetriever retriever; private final NextBlockRetriever retriever;
private volatile boolean processBlocks = false; private volatile boolean processBlocks = false;
public NetworkHttpStreamSender(NextBlockRetriever retriever) { public NetworkHttpStreamSender(NextBlockRetriever retriever, String room, Dimension screenDim, Dimension blockDim) {
this.retriever = retriever; this.retriever = retriever;
this.room = room;
this.screenDim = screenDim;
this.blockDim = blockDim;
} }
public void connect(String host) throws ConnectionException { public void connect(String host) throws ConnectionException {
@ -74,11 +79,10 @@ public class NetworkHttpStreamSender implements Runnable {
} }
} }
public void sendStartStreamMessage(String room, Dimension screen, Dimension block) { public void sendStartStreamMessage() {
this.room = room;
try { try {
openConnection(); openConnection();
sendCaptureStartEvent(screen, block); sendCaptureStartEvent(screenDim, blockDim);
} catch (ConnectionException e) { } catch (ConnectionException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
@ -142,13 +146,15 @@ public class NetworkHttpStreamSender implements Runnable {
processBlocks = true; processBlocks = true;
while (processBlocks) { while (processBlocks) {
Block block = retriever.fetchNextBlockToSend(); EncodedBlockData block;
EncodedBlockData ebd = block.encode(); try {
if (ebd.hasChanged()) { block = retriever.fetchNextBlockToSend();
BlockVideoData bv = new BlockVideoData(room, ebd.getPosition(), ebd.getVideoData(), ebd.isKeyFrame()); BlockVideoData bv = new BlockVideoData(room, block.getPosition(), block.getVideoData(), false);
sendBlockData(bv);
sendBlockData(bv); } catch (InterruptedException e) {
} // TODO Auto-generated catch block
e.printStackTrace();
}
} }
} }

View File

@ -26,8 +26,6 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.bigbluebutton.deskshare.client.blocks.Block;
import org.bigbluebutton.deskshare.common.Dimension; import org.bigbluebutton.deskshare.common.Dimension;
public class NetworkSocketStreamSender implements Runnable { public class NetworkSocketStreamSender implements Runnable {
@ -42,8 +40,11 @@ public class NetworkSocketStreamSender implements Runnable {
private final NextBlockRetriever retriever; private final NextBlockRetriever retriever;
private volatile boolean processBlocks = false; private volatile boolean processBlocks = false;
public NetworkSocketStreamSender(NextBlockRetriever retriever) { public NetworkSocketStreamSender(NextBlockRetriever retriever, String room, Dimension screenDim, Dimension blockDim) {
this.retriever = retriever; this.retriever = retriever;
this.room = room;
this.screenDim = screenDim;
this.blockDim = blockDim;
} }
public void connect(String host) throws ConnectionException { public void connect(String host) throws ConnectionException {
@ -60,10 +61,7 @@ public class NetworkSocketStreamSender implements Runnable {
} }
} }
public void sendStartStreamMessage(String room, Dimension screen, Dimension block) { public void sendStartStreamMessage() {
this.room = room;
screenDim = screen;
blockDim = block;
try { try {
ByteArrayOutputStream dataToSend = new ByteArrayOutputStream(); ByteArrayOutputStream dataToSend = new ByteArrayOutputStream();
dataToSend.reset(); dataToSend.reset();
@ -121,12 +119,16 @@ public class NetworkSocketStreamSender implements Runnable {
public void run() { public void run() {
processBlocks = true; processBlocks = true;
while (processBlocks) { while (processBlocks) {
Block block = retriever.fetchNextBlockToSend(); EncodedBlockData block;
EncodedBlockData ebd = block.encode(); try {
if (ebd.hasChanged()) { block = retriever.fetchNextBlockToSend();
BlockVideoData bv = new BlockVideoData(room, ebd.getPosition(), ebd.getVideoData(), ebd.isKeyFrame()); BlockVideoData bv = new BlockVideoData(room, block.getPosition(), block.getVideoData(), false /* should remove later */);
System.out.println("Sending block " + block.getPosition());
sendBlock(bv); sendBlock(bv);
} } catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
} }

View File

@ -21,36 +21,33 @@
*/ */
package org.bigbluebutton.deskshare.client.net; package org.bigbluebutton.deskshare.client.net;
import java.util.Map; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import net.jcip.annotations.GuardedBy; import java.util.concurrent.LinkedBlockingQueue;
import net.jcip.annotations.ThreadSafe; import net.jcip.annotations.ThreadSafe;
import org.bigbluebutton.deskshare.common.Dimension;
import org.bigbluebutton.deskshare.client.blocks.Block;
import org.bigbluebutton.deskshare.client.blocks.BlockManager;
@ThreadSafe @ThreadSafe
public class NetworkStreamSender implements NextBlockRetriever { public class NetworkStreamSender implements NextBlockRetriever {
private BlockManager blockManager; private ExecutorService executor;
private ExecutorService executor; private final BlockingQueue<EncodedBlockData> blockDataQ = new LinkedBlockingQueue<EncodedBlockData>();
@GuardedBy("this") private int nextBlockToSend = 1;
private final Map<Integer, Block> blocksMap;
private final int numThreads; private final int numThreads;
private final String host; private final String host;
private final String room; private final String room;
private NetworkSocketStreamSender[] senders; private NetworkSocketStreamSender[] senders;
private NetworkHttpStreamSender httpSender; private NetworkHttpStreamSender[] httpSenders;
private boolean tunneling = false; private boolean tunneling = false;
private int numRunningThreads = 0; private int numRunningThreads = 0;
private Dimension screenDim;
public NetworkStreamSender(BlockManager blockManager, String host, String room) { private Dimension blockDim;
this.blockManager = blockManager;
blocksMap = blockManager.getBlocks(); public NetworkStreamSender(String host, String room, Dimension screenDim, Dimension blockDim) {
this.host = host; this.host = host;
this.room = room; this.room = room;
this.screenDim = screenDim;
this.blockDim = blockDim;
numThreads = Runtime.getRuntime().availableProcessors(); numThreads = Runtime.getRuntime().availableProcessors();
System.out.println("Starting up " + numThreads + " sender threads."); System.out.println("Starting up " + numThreads + " sender threads.");
@ -73,6 +70,7 @@ public class NetworkStreamSender implements NextBlockRetriever {
System.out.println("Trying http tunneling"); System.out.println("Trying http tunneling");
if (tryHttpTunneling()) { if (tryHttpTunneling()) {
tunneling = true; tunneling = true;
httpSenders = new NetworkHttpStreamSender[numThreads];
return true; return true;
} }
} else { } else {
@ -83,28 +81,32 @@ public class NetworkStreamSender implements NextBlockRetriever {
} }
private void createSender(int i) throws ConnectionException { private void createSender(int i) throws ConnectionException {
senders[i] = new NetworkSocketStreamSender(this); senders[i] = new NetworkSocketStreamSender(this, room, screenDim, blockDim);
senders[i].connect(host); senders[i].connect(host);
} }
public void send(EncodedBlockData data) {
blockDataQ.offer(data);
}
public void start() { public void start() {
if (tunneling) { for (int i = 0; i < numRunningThreads; i++) {
httpSender.sendStartStreamMessage(room, blockManager.getScreenDim(), blockManager.getBlockDim()); if (tunneling) {
executor.execute(httpSender); httpSenders[i].sendStartStreamMessage();
} else { executor.execute(httpSenders[i]);
for (int i = 0; i < numRunningThreads; i++) { } else {
senders[i].sendStartStreamMessage(room, blockManager.getScreenDim(), blockManager.getBlockDim()); senders[i].sendStartStreamMessage();
executor.execute(senders[i]); executor.execute(senders[i]);
} }
} }
} }
public void stop() throws ConnectionException { public void stop() throws ConnectionException {
System.out.println("Stopping network sender"); System.out.println("Stopping network sender");
if (tunneling) { for (int i = 0; i < numRunningThreads; i++) {
httpSender.disconnect(); if (tunneling) {
} else { httpSenders[i].disconnect();
for (int i = 0; i < numRunningThreads; i++) { } else {
senders[i].disconnect(); senders[i].disconnect();
} }
} }
@ -113,7 +115,7 @@ public class NetworkStreamSender implements NextBlockRetriever {
} }
private boolean tryHttpTunneling() { private boolean tryHttpTunneling() {
httpSender = new NetworkHttpStreamSender(this); NetworkHttpStreamSender httpSender = new NetworkHttpStreamSender(this, room, screenDim, blockDim);
try { try {
httpSender.connect(host); httpSender.connect(host);
return true; return true;
@ -123,14 +125,7 @@ public class NetworkStreamSender implements NextBlockRetriever {
return false; return false;
} }
public Block fetchNextBlockToSend() { public EncodedBlockData fetchNextBlockToSend() throws InterruptedException {
synchronized(this) { return (EncodedBlockData) blockDataQ.take();
//Block block = blocksMap.get(new Integer(nextBlockToSend));
Block block = blockManager.getBlock(nextBlockToSend);
// System.out.println("Fetched block " + nextBlockToSend);
nextBlockToSend++;
if (nextBlockToSend > blocksMap.size()) nextBlockToSend = 1;
return block;
}
} }
} }

View File

@ -21,9 +21,6 @@
*/ */
package org.bigbluebutton.deskshare.client.net; package org.bigbluebutton.deskshare.client.net;
import org.bigbluebutton.deskshare.client.blocks.Block;
public interface NextBlockRetriever { public interface NextBlockRetriever {
public EncodedBlockData fetchNextBlockToSend() throws InterruptedException;
public Block fetchNextBlockToSend();
} }

View File

@ -84,11 +84,11 @@ public final class ScreenVideoEncoder {
return pixels; return pixels;
} }
public static byte[] encodePixels(int pixels[], int width, int height, boolean isRedTile, boolean isKeyFrame) { public static byte[] encodePixels(int pixels[], int width, int height) {
changePixelScanFromBottomLeftToTopRight(pixels, width, height); changePixelScanFromBottomLeftToTopRight(pixels, width, height);
byte[] bgrPixels = convertFromRGBtoBGR(pixels, isRedTile, isKeyFrame); byte[] bgrPixels = convertFromRGBtoBGR(pixels);
byte[] compressedPixels = compressUsingZlib(bgrPixels); byte[] compressedPixels = compressUsingZlib(bgrPixels);
@ -165,7 +165,7 @@ public final class ScreenVideoEncoder {
* @param pixels * @param pixels
* @return pixels in BGR order * @return pixels in BGR order
*/ */
private static byte[] convertFromRGBtoBGR(int[] pixels, boolean isRedTile, boolean isKeyFrame) { private static byte[] convertFromRGBtoBGR(int[] pixels) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
byte[] rgbPixels = new byte[pixels.length * 3]; byte[] rgbPixels = new byte[pixels.length * 3];
int position = 0; int position = 0;
@ -175,21 +175,10 @@ public final class ScreenVideoEncoder {
byte green = (byte) ((pixels[i] >> 8) & 0xff); byte green = (byte) ((pixels[i] >> 8) & 0xff);
byte blue = (byte) (pixels[i] & 0xff); byte blue = (byte) (pixels[i] & 0xff);
if (isRedTile && !isKeyFrame) { // Sequence should be BGR
// Sequence should be BGR rgbPixels[position++] = blue;
rgbPixels[position++] = 0; rgbPixels[position++] = green;
rgbPixels[position++] = 0; rgbPixels[position++] = red;
rgbPixels[position++] = (byte) 0xff;
} else if (isRedTile && isKeyFrame) {
rgbPixels[position++] = (byte) 0xff;
rgbPixels[position++] = 0;
rgbPixels[position++] = 0;
} else {
// Sequence should be BGR
rgbPixels[position++] = blue;
rgbPixels[position++] = green;
rgbPixels[position++] = red;
}
} }
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();