- 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.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[]) {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

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.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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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();