- 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:
parent
895ff7d53b
commit
fbe76ed23a
@ -29,12 +29,11 @@ import java.io.ByteArrayOutputStream;
|
||||
import org.bigbluebutton.deskshare.client.blocks.BlockManager;
|
||||
import org.bigbluebutton.deskshare.client.blocks.ChangedBlocksListener;
|
||||
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.common.Dimension;
|
||||
import netscape.javascript.*;
|
||||
|
||||
public class DeskShareApplet extends Applet implements IScreenCaptureListener, ChangedBlocksListener {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private ScreenCaptureTaker captureTaker;
|
||||
private ScreenCapture capture;
|
||||
@ -63,9 +62,6 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
|
||||
y = Integer.parseInt(getParameter("Y"));
|
||||
room = getParameter("ROOM");
|
||||
host = getParameter("IP");
|
||||
|
||||
String t = getParameter("TUNNEL");
|
||||
System.out.println("Tunnel param " + t);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@ -76,15 +72,13 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
|
||||
if (senderStarted)
|
||||
sender.stop();
|
||||
} catch (ConnectionException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
System.out.println("RunnerApplet start()");
|
||||
|
||||
System.out.println("Deskshare Applet start");
|
||||
startCapture();
|
||||
}
|
||||
|
||||
@ -98,7 +92,7 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
|
||||
blockManager.addListener(this);
|
||||
blockManager.initialize(screenDim, tileDim);
|
||||
|
||||
sender = new NetworkStreamSender(blockManager, host, room);
|
||||
sender = new NetworkStreamSender(host, room, screenDim, tileDim);
|
||||
connected = sender.connect();
|
||||
if (connected) {
|
||||
captureTaker.addListener(this);
|
||||
@ -107,52 +101,31 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
|
||||
captureTakerThread = new Thread(captureTaker, "ScreenCaptureTaker");
|
||||
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
|
||||
* It is very important that the connection to the server is closed at this point. That way the server knows to
|
||||
* close the stream.
|
||||
*/
|
||||
public void destroy(){
|
||||
public void destroy() {
|
||||
try {
|
||||
sender.stop();
|
||||
} catch (ConnectionException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
public void setScreenCoordinates(int x, int y){
|
||||
public void setScreenCoordinates(int x, int y) {
|
||||
capture.setX(x);
|
||||
capture.setY(y);
|
||||
}
|
||||
|
||||
public void onScreenCaptured(BufferedImage screen, boolean isKeyFrame) {
|
||||
blockManager.processCapturedScreen(screen, isKeyFrame);
|
||||
public void onScreenCaptured(BufferedImage screen) {
|
||||
blockManager.processCapturedScreen(screen);
|
||||
}
|
||||
|
||||
|
||||
@ -161,11 +134,8 @@ public class DeskShareApplet extends Applet implements IScreenCaptureListener, C
|
||||
destroy();
|
||||
}
|
||||
|
||||
public void onChangedTiles(ByteArrayOutputStream pixelData, boolean isKeyFrame) {
|
||||
if (! senderStarted) {
|
||||
sender.start();
|
||||
senderStarted = true;
|
||||
}
|
||||
public void onChangedBlock(EncodedBlockData encodedData) {
|
||||
sender.send(encodedData);
|
||||
}
|
||||
|
||||
static public void main (String argv[]) {
|
||||
|
@ -25,5 +25,5 @@ import java.awt.image.BufferedImage;
|
||||
|
||||
public interface IScreenCaptureListener {
|
||||
public void screenCaptureStopped();
|
||||
public void onScreenCaptured(BufferedImage screen, boolean isKeyFrame);
|
||||
public void onScreenCaptured(BufferedImage screen);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public class ScreenCapture {
|
||||
private Rectangle screenBounds;
|
||||
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.height = screenHeight;
|
||||
try{
|
||||
@ -54,26 +54,26 @@ public class ScreenCapture {
|
||||
this.screenBounds = new Rectangle(x, y, this.width, this.height);
|
||||
}
|
||||
|
||||
public BufferedImage takeSingleSnapshot(){
|
||||
public BufferedImage takeSingleSnapshot() {
|
||||
return robot.createScreenCapture(this.screenBounds);
|
||||
}
|
||||
|
||||
public int getScreenshotWidth(){
|
||||
public int getScreenshotWidth() {
|
||||
return toolkit.getScreenSize().width;
|
||||
}
|
||||
|
||||
public int getScreenshotHeight(){
|
||||
public int getScreenshotHeight() {
|
||||
return toolkit.getScreenSize().height;
|
||||
}
|
||||
|
||||
public void setWidth(int width){
|
||||
public void setWidth(int width) {
|
||||
int screenWidth = toolkit.getScreenSize().width;
|
||||
if (width > screenWidth) this.width = screenWidth;
|
||||
else this.width = width;
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
public void setHeight(int height){
|
||||
public void setHeight(int height) {
|
||||
int screenHeight = toolkit.getScreenSize().height;
|
||||
if (height > screenHeight) {
|
||||
this.height = screenHeight;
|
||||
@ -84,29 +84,29 @@ public class ScreenCapture {
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
public void setX(int x){
|
||||
public void setX(int x) {
|
||||
this.x = x;
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
public void setY(int y){
|
||||
public void setY(int y) {
|
||||
this.y = y;
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
public void updateBounds(){
|
||||
public void updateBounds() {
|
||||
this.screenBounds = new Rectangle(x,y,width,height);
|
||||
}
|
||||
|
||||
public int getWidth(){
|
||||
public int getWidth() {
|
||||
return this.width;
|
||||
}
|
||||
|
||||
public int getHeight(){
|
||||
public int getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
public int getProperFrameRate(){
|
||||
public int getProperFrameRate() {
|
||||
long area = width*height;
|
||||
if (area > 1000000) return 1;
|
||||
else if (area > 600000) return 2;
|
||||
@ -115,11 +115,11 @@ public class ScreenCapture {
|
||||
else return 10;
|
||||
}
|
||||
|
||||
public int getVideoWidth(){
|
||||
public int getVideoWidth() {
|
||||
return videoWidth;
|
||||
}
|
||||
|
||||
public int getVideoHeight(){
|
||||
public int getVideoHeight() {
|
||||
return videoHeight;
|
||||
}
|
||||
|
||||
|
@ -23,11 +23,8 @@ package org.bigbluebutton.deskshare.client;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class ScreenCaptureTaker implements Runnable {
|
||||
|
||||
public class ScreenCaptureTaker implements Runnable {
|
||||
private ScreenCapture capture;
|
||||
private int timeBase;
|
||||
private int frameCount = 0;
|
||||
private IScreenCaptureListener listeners;
|
||||
|
||||
private volatile boolean startCapture = false;
|
||||
@ -35,64 +32,34 @@ public class ScreenCaptureTaker implements Runnable {
|
||||
public ScreenCaptureTaker(ScreenCapture capture){
|
||||
System.out.println("Capture thread constructor.");
|
||||
this.capture = capture;
|
||||
this.timeBase = 1000 / capture.getProperFrameRate();
|
||||
}
|
||||
|
||||
public void run(){
|
||||
while (startCapture){
|
||||
System.out.println("----- Taking screen capture -----");
|
||||
long snapshotTime = System.currentTimeMillis();
|
||||
BufferedImage image = capture.takeSingleSnapshot();
|
||||
long snapshotEnd = System.currentTimeMillis();
|
||||
// System.out.println("Snapshot time = " + (snapshotEnd - snapshotTime) + "ms.");
|
||||
notifyListeners(image);
|
||||
long completeTime = System.currentTimeMillis();
|
||||
// System.out.println("Processing time = " + (completeTime - snapshotTime) + "ms.");
|
||||
try{
|
||||
//Thread.sleep(timeBase);
|
||||
Thread.sleep(500);
|
||||
System.out.println("Going to sleeeeeep.");
|
||||
Thread.sleep(1000);
|
||||
System.out.println("Just woke up.");
|
||||
} catch (Exception e){
|
||||
System.out.println("Exception sleeping.");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Stopping screen capture.");
|
||||
|
||||
System.out.println("Stopping screen capture.");
|
||||
listeners.screenCaptureStopped();
|
||||
}
|
||||
|
||||
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) {
|
||||
// listeners.add(listener);
|
||||
listeners = listener;
|
||||
}
|
||||
|
||||
public void removeListener(IScreenCaptureListener listener) {
|
||||
// listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void setCapture(boolean capture) {
|
||||
startCapture = capture;
|
||||
|
@ -36,16 +36,9 @@ public final class Block {
|
||||
private final Adler32 checksum;
|
||||
private final Dimension dim;
|
||||
private final int position;
|
||||
private final Point location;
|
||||
|
||||
private boolean isKeyFrame = false;
|
||||
private final Point location;
|
||||
private int[] pixels;
|
||||
private EncodedBlockData encodedBlockData;
|
||||
|
||||
private int nextForceUpdate = 10000;
|
||||
private final static int MIN_DURATION = 5000;
|
||||
private final static int MAX_DURATION = 10000;
|
||||
private long lastChanged;
|
||||
private boolean firstTime = true;
|
||||
|
||||
Block(Dimension dim, int position, Point location) {
|
||||
checksum = new Adler32();
|
||||
@ -54,10 +47,8 @@ public final class Block {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public void updateBlock(BufferedImage capturedScreen, boolean isKeyFrame)
|
||||
{
|
||||
synchronized(this) {
|
||||
this.isKeyFrame = isKeyFrame;
|
||||
public void updateBlock(BufferedImage capturedScreen) {
|
||||
synchronized(this) {
|
||||
try {
|
||||
pixels = ScreenVideoEncoder.getPixels(capturedScreen, getX(), getY(), getWidth(), getHeight());
|
||||
} catch (PixelExtractException e) {
|
||||
@ -66,82 +57,44 @@ public final class Block {
|
||||
}
|
||||
}
|
||||
|
||||
public EncodedBlockData encode() {
|
||||
int[] pixelsCopy = new int[pixels.length];
|
||||
synchronized (this) {
|
||||
System.arraycopy(pixels, 0, pixelsCopy, 0, pixels.length);
|
||||
}
|
||||
|
||||
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;
|
||||
public boolean hasChanged() {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
return true;
|
||||
}
|
||||
return ! checksumSame();
|
||||
}
|
||||
|
||||
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;
|
||||
oldsum = checksum.getValue();
|
||||
calcChecksum(pixelsCopy);
|
||||
calcChecksum();
|
||||
return (oldsum == checksum.getValue());
|
||||
}
|
||||
|
||||
private boolean forceUpdate() {
|
||||
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[])
|
||||
{
|
||||
|
||||
private void calcChecksum() {
|
||||
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(pixelsCopy[i * width + j]);
|
||||
if ((i * width + j) % 5 == 0)
|
||||
checksum.update(pixels[i * width + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
public int getWidth() {
|
||||
return new Integer(dim.getWidth()).intValue();
|
||||
}
|
||||
|
||||
public int getHeight()
|
||||
{
|
||||
public int getHeight() {
|
||||
return new Integer(dim.getHeight()).intValue();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -23,18 +23,11 @@ package org.bigbluebutton.deskshare.client.blocks;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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.client.net.EncodedBlockData;
|
||||
|
||||
public class BlockManager {
|
||||
private final Map<Integer, Block> blocksMap;
|
||||
@ -48,12 +41,9 @@ public class BlockManager {
|
||||
private ByteArrayOutputStream encodedPixelStream = new ByteArrayOutputStream();
|
||||
private Dimension screenDim, blockDim;
|
||||
|
||||
// private ScreenVideoBlockEncoder encoder;
|
||||
|
||||
public BlockManager() {
|
||||
blocksMap = new HashMap<Integer, Block>();
|
||||
unmodifiableBlocksMap = Collections.unmodifiableMap(blocksMap);
|
||||
// encoder = new ScreenVideoBlockEncoder();
|
||||
}
|
||||
|
||||
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();
|
||||
// Block[] blocks = new Block[numRows * numColumns];
|
||||
int index = 0;
|
||||
int numberOfBlocks = numColumns * numRows;
|
||||
for (int position = 1; position <= numberOfBlocks; position++) {
|
||||
Block block = blocksMap.get(new Integer(position));
|
||||
block.updateBlock(capturedScreen, isKeyFrame);
|
||||
|
||||
|
||||
// blocks[index++] = block;
|
||||
block.updateBlock(capturedScreen);
|
||||
if (block.hasChanged()) {
|
||||
System.out.println("Block " + block.getPosition() + " has changed.");
|
||||
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();
|
||||
System.out.println("ProcessCapturedScreen took " + (end-start) + " ms.");
|
||||
}
|
||||
/*
|
||||
private void sendEncodedData(boolean isKeyFrame) {
|
||||
long start = System.currentTimeMillis();
|
||||
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);
|
||||
|
||||
private void notifyChangedBlockListener(EncodedBlockData encodedData) {
|
||||
listeners.onChangedBlock(encodedData);
|
||||
}
|
||||
|
||||
|
||||
@ -135,9 +87,7 @@ public class BlockManager {
|
||||
listeners = listener;
|
||||
}
|
||||
|
||||
|
||||
public void removeListener(ChangedBlocksListener listener) {
|
||||
//listeners.remove(listener);
|
||||
listeners = null;
|
||||
}
|
||||
|
||||
@ -145,13 +95,11 @@ public class BlockManager {
|
||||
return (Block) blocksMap.get(Integer.valueOf(position));
|
||||
}
|
||||
|
||||
public int getRowCount()
|
||||
{
|
||||
public int getRowCount() {
|
||||
return numRows;
|
||||
}
|
||||
|
||||
public int getColumnCount()
|
||||
{
|
||||
public int getColumnCount() {
|
||||
return numColumns;
|
||||
}
|
||||
|
||||
@ -162,11 +110,4 @@ public class BlockManager {
|
||||
public Dimension getBlockDim() {
|
||||
return blockDim;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a read-only "live" view of the blocks;
|
||||
*/
|
||||
public Map<Integer, Block> getBlocks() {
|
||||
return unmodifiableBlocksMap;
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,8 @@
|
||||
*/
|
||||
package org.bigbluebutton.deskshare.client.blocks;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import org.bigbluebutton.deskshare.client.net.EncodedBlockData;
|
||||
|
||||
public interface ChangedBlocksListener {
|
||||
|
||||
public void onChangedTiles(ByteArrayOutputStream pixelData, boolean isKeyFrame);
|
||||
public void onChangedBlock(EncodedBlockData encodedData);
|
||||
}
|
||||
|
@ -22,18 +22,13 @@
|
||||
package org.bigbluebutton.deskshare.client.net;
|
||||
|
||||
public class EncodedBlockData {
|
||||
|
||||
private final byte[] videoData;
|
||||
private final boolean keyFrame;
|
||||
private final int position;
|
||||
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.videoData = videoData;
|
||||
this.keyFrame = keyFrame;
|
||||
this.hasChanged = hasChanged;
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@ -44,16 +39,8 @@ public class EncodedBlockData {
|
||||
public byte[] getVideoData() {
|
||||
return videoData;
|
||||
}
|
||||
|
||||
public boolean isKeyFrame() {
|
||||
return keyFrame;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public boolean hasChanged() {
|
||||
return hasChanged;
|
||||
}
|
||||
}
|
||||
|
@ -40,12 +40,17 @@ public class NetworkHttpStreamSender implements Runnable {
|
||||
private URL url;
|
||||
URLConnection conn;
|
||||
private String room;
|
||||
private Dimension screenDim;
|
||||
private Dimension blockDim;
|
||||
private final NextBlockRetriever retriever;
|
||||
private volatile boolean processBlocks = false;
|
||||
|
||||
|
||||
public NetworkHttpStreamSender(NextBlockRetriever retriever) {
|
||||
public NetworkHttpStreamSender(NextBlockRetriever retriever, String room, Dimension screenDim, Dimension blockDim) {
|
||||
this.retriever = retriever;
|
||||
this.room = room;
|
||||
this.screenDim = screenDim;
|
||||
this.blockDim = blockDim;
|
||||
}
|
||||
|
||||
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) {
|
||||
this.room = room;
|
||||
public void sendStartStreamMessage() {
|
||||
try {
|
||||
openConnection();
|
||||
sendCaptureStartEvent(screen, block);
|
||||
sendCaptureStartEvent(screenDim, blockDim);
|
||||
} catch (ConnectionException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
@ -142,13 +146,15 @@ public class NetworkHttpStreamSender implements Runnable {
|
||||
processBlocks = true;
|
||||
|
||||
while (processBlocks) {
|
||||
Block block = retriever.fetchNextBlockToSend();
|
||||
EncodedBlockData ebd = block.encode();
|
||||
if (ebd.hasChanged()) {
|
||||
BlockVideoData bv = new BlockVideoData(room, ebd.getPosition(), ebd.getVideoData(), ebd.isKeyFrame());
|
||||
|
||||
sendBlockData(bv);
|
||||
}
|
||||
EncodedBlockData block;
|
||||
try {
|
||||
block = retriever.fetchNextBlockToSend();
|
||||
BlockVideoData bv = new BlockVideoData(room, block.getPosition(), block.getVideoData(), false);
|
||||
sendBlockData(bv);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,6 @@ import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.bigbluebutton.deskshare.client.blocks.Block;
|
||||
import org.bigbluebutton.deskshare.common.Dimension;
|
||||
|
||||
public class NetworkSocketStreamSender implements Runnable {
|
||||
@ -42,8 +40,11 @@ public class NetworkSocketStreamSender implements Runnable {
|
||||
private final NextBlockRetriever retriever;
|
||||
private volatile boolean processBlocks = false;
|
||||
|
||||
public NetworkSocketStreamSender(NextBlockRetriever retriever) {
|
||||
public NetworkSocketStreamSender(NextBlockRetriever retriever, String room, Dimension screenDim, Dimension blockDim) {
|
||||
this.retriever = retriever;
|
||||
this.room = room;
|
||||
this.screenDim = screenDim;
|
||||
this.blockDim = blockDim;
|
||||
}
|
||||
|
||||
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) {
|
||||
this.room = room;
|
||||
screenDim = screen;
|
||||
blockDim = block;
|
||||
public void sendStartStreamMessage() {
|
||||
try {
|
||||
ByteArrayOutputStream dataToSend = new ByteArrayOutputStream();
|
||||
dataToSend.reset();
|
||||
@ -121,12 +119,16 @@ public class NetworkSocketStreamSender implements Runnable {
|
||||
public void run() {
|
||||
processBlocks = true;
|
||||
while (processBlocks) {
|
||||
Block block = retriever.fetchNextBlockToSend();
|
||||
EncodedBlockData ebd = block.encode();
|
||||
if (ebd.hasChanged()) {
|
||||
BlockVideoData bv = new BlockVideoData(room, ebd.getPosition(), ebd.getVideoData(), ebd.isKeyFrame());
|
||||
EncodedBlockData block;
|
||||
try {
|
||||
block = retriever.fetchNextBlockToSend();
|
||||
BlockVideoData bv = new BlockVideoData(room, block.getPosition(), block.getVideoData(), false /* should remove later */);
|
||||
System.out.println("Sending block " + block.getPosition());
|
||||
sendBlock(bv);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,36 +21,33 @@
|
||||
*/
|
||||
package org.bigbluebutton.deskshare.client.net;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import net.jcip.annotations.GuardedBy;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import net.jcip.annotations.ThreadSafe;
|
||||
|
||||
import org.bigbluebutton.deskshare.client.blocks.Block;
|
||||
import org.bigbluebutton.deskshare.client.blocks.BlockManager;
|
||||
import org.bigbluebutton.deskshare.common.Dimension;
|
||||
|
||||
@ThreadSafe
|
||||
public class NetworkStreamSender implements NextBlockRetriever {
|
||||
private BlockManager blockManager;
|
||||
private ExecutorService executor;
|
||||
|
||||
@GuardedBy("this") private int nextBlockToSend = 1;
|
||||
private final Map<Integer, Block> blocksMap;
|
||||
private ExecutorService executor;
|
||||
private final BlockingQueue<EncodedBlockData> blockDataQ = new LinkedBlockingQueue<EncodedBlockData>();
|
||||
|
||||
private final int numThreads;
|
||||
private final String host;
|
||||
private final String room;
|
||||
private NetworkSocketStreamSender[] senders;
|
||||
private NetworkHttpStreamSender httpSender;
|
||||
private NetworkHttpStreamSender[] httpSenders;
|
||||
private boolean tunneling = false;
|
||||
private int numRunningThreads = 0;
|
||||
|
||||
public NetworkStreamSender(BlockManager blockManager, String host, String room) {
|
||||
this.blockManager = blockManager;
|
||||
blocksMap = blockManager.getBlocks();
|
||||
private Dimension screenDim;
|
||||
private Dimension blockDim;
|
||||
|
||||
public NetworkStreamSender(String host, String room, Dimension screenDim, Dimension blockDim) {
|
||||
this.host = host;
|
||||
this.room = room;
|
||||
this.screenDim = screenDim;
|
||||
this.blockDim = blockDim;
|
||||
|
||||
numThreads = Runtime.getRuntime().availableProcessors();
|
||||
System.out.println("Starting up " + numThreads + " sender threads.");
|
||||
@ -73,6 +70,7 @@ public class NetworkStreamSender implements NextBlockRetriever {
|
||||
System.out.println("Trying http tunneling");
|
||||
if (tryHttpTunneling()) {
|
||||
tunneling = true;
|
||||
httpSenders = new NetworkHttpStreamSender[numThreads];
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@ -83,28 +81,32 @@ public class NetworkStreamSender implements NextBlockRetriever {
|
||||
}
|
||||
|
||||
private void createSender(int i) throws ConnectionException {
|
||||
senders[i] = new NetworkSocketStreamSender(this);
|
||||
senders[i] = new NetworkSocketStreamSender(this, room, screenDim, blockDim);
|
||||
senders[i].connect(host);
|
||||
}
|
||||
|
||||
public void send(EncodedBlockData data) {
|
||||
blockDataQ.offer(data);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (tunneling) {
|
||||
httpSender.sendStartStreamMessage(room, blockManager.getScreenDim(), blockManager.getBlockDim());
|
||||
executor.execute(httpSender);
|
||||
} else {
|
||||
for (int i = 0; i < numRunningThreads; i++) {
|
||||
senders[i].sendStartStreamMessage(room, blockManager.getScreenDim(), blockManager.getBlockDim());
|
||||
executor.execute(senders[i]);
|
||||
}
|
||||
for (int i = 0; i < numRunningThreads; i++) {
|
||||
if (tunneling) {
|
||||
httpSenders[i].sendStartStreamMessage();
|
||||
executor.execute(httpSenders[i]);
|
||||
} else {
|
||||
senders[i].sendStartStreamMessage();
|
||||
executor.execute(senders[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() throws ConnectionException {
|
||||
System.out.println("Stopping network sender");
|
||||
if (tunneling) {
|
||||
httpSender.disconnect();
|
||||
} else {
|
||||
for (int i = 0; i < numRunningThreads; i++) {
|
||||
for (int i = 0; i < numRunningThreads; i++) {
|
||||
if (tunneling) {
|
||||
httpSenders[i].disconnect();
|
||||
} else {
|
||||
senders[i].disconnect();
|
||||
}
|
||||
}
|
||||
@ -113,7 +115,7 @@ public class NetworkStreamSender implements NextBlockRetriever {
|
||||
}
|
||||
|
||||
private boolean tryHttpTunneling() {
|
||||
httpSender = new NetworkHttpStreamSender(this);
|
||||
NetworkHttpStreamSender httpSender = new NetworkHttpStreamSender(this, room, screenDim, blockDim);
|
||||
try {
|
||||
httpSender.connect(host);
|
||||
return true;
|
||||
@ -123,14 +125,7 @@ public class NetworkStreamSender implements NextBlockRetriever {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Block fetchNextBlockToSend() {
|
||||
synchronized(this) {
|
||||
//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;
|
||||
}
|
||||
public EncodedBlockData fetchNextBlockToSend() throws InterruptedException {
|
||||
return (EncodedBlockData) blockDataQ.take();
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,6 @@
|
||||
*/
|
||||
package org.bigbluebutton.deskshare.client.net;
|
||||
|
||||
import org.bigbluebutton.deskshare.client.blocks.Block;
|
||||
|
||||
public interface NextBlockRetriever {
|
||||
|
||||
public Block fetchNextBlockToSend();
|
||||
public EncodedBlockData fetchNextBlockToSend() throws InterruptedException;
|
||||
}
|
||||
|
@ -84,11 +84,11 @@ public final class ScreenVideoEncoder {
|
||||
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);
|
||||
|
||||
byte[] bgrPixels = convertFromRGBtoBGR(pixels, isRedTile, isKeyFrame);
|
||||
byte[] bgrPixels = convertFromRGBtoBGR(pixels);
|
||||
|
||||
byte[] compressedPixels = compressUsingZlib(bgrPixels);
|
||||
|
||||
@ -165,7 +165,7 @@ public final class ScreenVideoEncoder {
|
||||
* @param pixels
|
||||
* @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();
|
||||
byte[] rgbPixels = new byte[pixels.length * 3];
|
||||
int position = 0;
|
||||
@ -175,21 +175,10 @@ public final class ScreenVideoEncoder {
|
||||
byte green = (byte) ((pixels[i] >> 8) & 0xff);
|
||||
byte blue = (byte) (pixels[i] & 0xff);
|
||||
|
||||
if (isRedTile && !isKeyFrame) {
|
||||
// Sequence should be BGR
|
||||
rgbPixels[position++] = 0;
|
||||
rgbPixels[position++] = 0;
|
||||
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;
|
||||
}
|
||||
// Sequence should be BGR
|
||||
rgbPixels[position++] = blue;
|
||||
rgbPixels[position++] = green;
|
||||
rgbPixels[position++] = red;
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
|
Loading…
Reference in New Issue
Block a user