You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2017/04/30 17:29:39 UTC

[27/50] [abbrv] openmeetings git commit: [OPENMEETINGS-551] basic video support is added

[OPENMEETINGS-551] basic video support is added


Project: http://git-wip-us.apache.org/repos/asf/openmeetings/repo
Commit: http://git-wip-us.apache.org/repos/asf/openmeetings/commit/838bfbe3
Tree: http://git-wip-us.apache.org/repos/asf/openmeetings/tree/838bfbe3
Diff: http://git-wip-us.apache.org/repos/asf/openmeetings/diff/838bfbe3

Branch: refs/heads/master
Commit: 838bfbe3b9987bd4c9cfadb39cb08a3b79fff66c
Parents: e227dd4
Author: Maxim Solodovnik <so...@apache.org>
Authored: Thu Apr 13 16:29:41 2017 +0000
Committer: Maxim Solodovnik <so...@apache.org>
Committed: Thu Apr 13 16:29:41 2017 +0000

----------------------------------------------------------------------
 .../remote/red5/ScopeApplicationAdapter.java    |   6 +-
 .../openmeetings/db/entity/basic/Client.java    |  64 +++++++++-
 openmeetings-flash/src/main/flex/main.mxml      | 122 ++++++++++++-------
 .../flex/org/apache/openmeetings/OmVideo.as     |  44 ++++---
 .../openmeetings/web/app/Application.java       |   2 +-
 .../openmeetings/web/common/MainPanel.java      |  56 +++++----
 .../apache/openmeetings/web/pages/HashPage.java |   5 +-
 .../apache/openmeetings/web/room/RoomPanel.html |   5 +
 .../apache/openmeetings/web/room/RoomPanel.java |  20 ++-
 .../openmeetings/web/room/VideoSettings.java    |   6 +-
 .../org/apache/openmeetings/web/room/room.js    |  94 ++++++++++++++
 .../apache/openmeetings/web/room/settings.js    |   7 +-
 .../web/room/sidebar/RoomSidebar.java           |  14 ++-
 .../web/util/ExtendedClientProperties.java      |  27 +++-
 openmeetings-web/src/main/webapp/css/room.css   |  19 ++-
 pom.xml                                         |   2 +-
 16 files changed, 373 insertions(+), 120 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java
----------------------------------------------------------------------
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java
index ab9d5ab..f0e5db7 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java
@@ -201,6 +201,9 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		String tcUrl = map.containsKey("tcUrl") ? (String)map.get("tcUrl") : "";
 		Map<String, Object> connParams = getConnParams(params);
 		String uid = (String)connParams.get("uid");
+		if ("noclient".equals(uid)) {
+			return true;
+		}
 		String securityCode = (String)connParams.get(SECURITY_CODE_PARAM);
 		String parentSid = (String)map.get("parentSid");
 		if (parentSid == null) {
@@ -249,9 +252,6 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			log.warn("No UIDs are provided, client is rejected");
 			return rejectClient();
 		}
-		if ("networktest".equals(uid)) {
-			return true;
-		}
 
 		if (map.containsKey("screenClient")) {
 			Client parent = sessionManager.getClientByPublicSID(parentSid, null);

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
----------------------------------------------------------------------
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
index 8ec7b27..0f8fc82 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
@@ -28,6 +28,10 @@ import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.wicket.protocol.ws.api.registry.IKey;
+import org.apache.wicket.util.string.Strings;
+
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
 
 /**
  * Temporary class, later will be merged with {@link org.apache.openmeetings.db.entity.room.Client}
@@ -65,6 +69,7 @@ public class Client implements IClient {
 	private int mic = -1;
 	private int width = 0;
 	private int height = 0;
+	private long broadcastId = -1;
 
 	public Client(String sessionId, int pageId, Long userId, UserDao dao) {
 		this.sessionId = sessionId;
@@ -157,6 +162,16 @@ public class Client implements IClient {
 		return activities;
 	}
 
+	public boolean hasAnyActivity(Activity... aa) {
+		boolean res = false;
+		if (aa != null) {
+			for (Activity a : aa) {
+				res |= activities.contains(a);
+			}
+		}
+		return res;
+	}
+
 	public boolean hasActivity(Activity a) {
 		return activities.contains(a);
 	}
@@ -243,8 +258,9 @@ public class Client implements IClient {
 		return cam;
 	}
 
-	public void setCam(int cam) {
+	public Client setCam(int cam) {
 		this.cam = cam;
+		return this;
 	}
 
 	public boolean isMicEnabled() {
@@ -255,32 +271,45 @@ public class Client implements IClient {
 		return mic;
 	}
 
-	public void setMic(int mic) {
+	public Client setMic(int mic) {
 		this.mic = mic;
+		return this;
 	}
 
 	public int getWidth() {
 		return width;
 	}
 
-	public void setWidth(int width) {
+	public Client setWidth(int width) {
 		this.width = width;
+		return this;
 	}
 
 	public int getHeight() {
 		return height;
 	}
 
-	public void setHeight(int height) {
+	public Client setHeight(int height) {
 		this.height = height;
+		return this;
 	}
 
 	public String getRemoteAddress() {
 		return remoteAddress;
 	}
 
-	public void setRemoteAddress(String remoteAddress) {
+	public Client setRemoteAddress(String remoteAddress) {
 		this.remoteAddress = remoteAddress;
+		return this;
+	}
+
+	public long getBroadcastId() {
+		return broadcastId;
+	}
+
+	public Client setBroadcastId(long broadcastId) {
+		this.broadcastId = broadcastId;
+		return this;
 	}
 
 	@Override
@@ -313,6 +342,31 @@ public class Client implements IClient {
 		return true;
 	}
 
+	public JSONObject toJson() {
+		JSONObject u = new JSONObject();
+		if (user != null) {
+			JSONObject a = new JSONObject();
+			u.put("firstName", user.getFirstname())
+				.put("lastName", user.getLastname())
+				.put("address", a);
+			if (user.getAddress() != null) {
+				if (Strings.isEmpty(user.getFirstname()) && Strings.isEmpty(user.getLastname())) {
+					a.put("email", user.getAddress().getEmail());
+				}
+				a.put("country", user.getAddress().getCountry());
+			}
+		}
+		return new JSONObject()
+				.put("user", u)
+				.put("uid", uid)
+				.put("rights", new JSONArray(rights))
+				.put("activities", new JSONArray(activities))
+				.put("pod", pod)
+				.put("broadcastId", broadcastId)
+				.put("width", width)
+				.put("height", height);
+	}
+
 	@Override
 	public String toString() {
 		return "Client [uid=" + uid + ", sessionId=" + sessionId + ", pageId=" + pageId + ", userId=" + user.getId() + ", roomId=" + roomId

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-flash/src/main/flex/main.mxml
----------------------------------------------------------------------
diff --git a/openmeetings-flash/src/main/flex/main.mxml b/openmeetings-flash/src/main/flex/main.mxml
index 1e3867b..d731972 100644
--- a/openmeetings-flash/src/main/flex/main.mxml
+++ b/openmeetings-flash/src/main/flex/main.mxml
@@ -60,46 +60,72 @@
 		}
 
 		private function appInit(evt:Event):void {
-			var tla:Object = FlexGlobals.topLevelApplication;
-			debug("appInit()", tla.parameters);
-			audioOnly = 'true' == tla.parameters['audioOnly'];
-			interview = 'true' == tla.parameters['interview'];
-			var _fps:int = parseInt(tla.parameters['fps']);
+			var params:Object = FlexGlobals.topLevelApplication.parameters;
+			debug("appInit()", params);
+			audioOnly = 'true' == params.audioOnly;
+			interview = 'true' == params.interview;
+			var _fps:int = parseInt(params.fps);
 			FPS = (isNaN(_fps) || _fps < 1 ? 30 : _fps);
-			video = new OmVideo(videoDisplay, tla.parameters);
-
-			ExternalInterface.addCallback("getDevices", function ():Object {
-				return {
-					cams: Camera.names
-					, mics: Microphone.names
-				};
-			});
-			ExternalInterface.addCallback("camChanged", function (val:int):void {
-				selectedCam = val;
-				camChanged(null);
-			});
-			ExternalInterface.addCallback("micChanged", function (val:int):void {
-				selectedMic = val;
-				camChanged(null);
-			});
-			ExternalInterface.addCallback("resChanged", function (width:int, height:int):void {
-				setResolution(width, height, true);
-			});
-			ExternalInterface.addCallback("close", function ():void {
-				video.reset();
-			});
-			ExternalInterface.addCallback("init", function (camIdx:int, micIdx:int, width:int, height:int):void {
-				selectedCam = camIdx;
-				selectedMic = micIdx;
-				setResolution(width, height, true);
-			});
-			ExternalInterface.addCallback("startRec", function ():void {
-				startTestRecording();
-			});
-			ExternalInterface.addCallback("play", function ():void {
-				playTestRecording();
-			});
-			ExternalInterface.call("VideoSettings.initSwf");
+			video = new OmVideo(videoDisplay, params);
+			switch (params.mode) {
+				case 'settings':
+				{
+					ExternalInterface.addCallback("getDevices", function ():Object {
+						return {
+							cams: Camera.names
+							, mics: Microphone.names
+						};
+					});
+					ExternalInterface.addCallback("camChanged", function (val:int):void {
+						selectedCam = val;
+						camChanged(null);
+					});
+					ExternalInterface.addCallback("micChanged", function (val:int):void {
+						selectedMic = val;
+						camChanged(null);
+					});
+					ExternalInterface.addCallback("resChanged", function (width:int, height:int):void {
+						setResolution(width, height, true);
+					});
+					ExternalInterface.addCallback("close", function ():void {
+						video.reset();
+					});
+					ExternalInterface.addCallback("init", function (camIdx:int, micIdx:int, width:int, height:int):void {
+						selectedCam = camIdx;
+						selectedMic = micIdx;
+						setResolution(width, height, true);
+					});
+					ExternalInterface.addCallback("startRec", function ():void {
+						startTestRecording();
+					});
+					ExternalInterface.addCallback("play", function ():void {
+						playTestRecording();
+					});
+					ExternalInterface.call("VideoSettings.initSwf");
+				}
+					break;
+				case OmVideo.BROADCAST:
+				{
+					selectedCam = params.cam;
+					selectedMic = params.mic;
+					video.resize(Math.max(300, params.width), Math.max(200, params.height));
+					attachCamera(function():void {
+						video.resize(params.width, params.height);
+						video.reset();
+						var cam:Camera = getCam();
+						video.attachCamera(cam);
+						video.broadcast(params.broadcastId, cam, getMic());
+						ExternalInterface.call("VideoManager.resetSize", params.uid);
+					});
+				}
+					break;
+				case OmVideo.PLAY:
+				{
+					video.resize(params.width, params.height);
+					video.play(params.broadcastId); // TODO audio/video
+				}
+					break;
+			}
 		}
 
 		private function uncaughtError(e:UncaughtErrorEvent):void {
@@ -160,7 +186,7 @@
 			return _camera;
 		}
 
-		private function attachCamera():void {
+		private function attachCamera(callback:Function):void {
 			if (!camAvail()) {
 				return;
 			}
@@ -179,13 +205,12 @@
 								debug("Unable to connect to active camera.");
 								break;
 							case 'Camera.Unmuted':
-								ExternalInterface.call("VideoSettings.allowRec", true);
-								_attachCamera(getCam());
+								callback();
 								break;
 						}
 					});
 				} else {
-					_attachCamera(cam);
+					callback();
 				}
 			} else {
 				var _mic:Microphone = getMic();
@@ -226,6 +251,11 @@
 			}
 		}
 
+		private function settingsCameraCallback():void {
+			ExternalInterface.call("VideoSettings.allowRec", true);
+			_attachCamera(getCam());
+		}
+
 		private function setResolution(width:int, height:int, attach:Boolean):void {
 			if (!interview) {
 				debug("onselect WxH :: " + width + "x" + height);
@@ -233,17 +263,17 @@
 				video.resize(width, height);
 
 				if (attach) {
-					attachCamera();
+					attachCamera(settingsCameraCallback);
 				}
 			}
 		}
 
 		private function camChanged(e:Event):void {
-			attachCamera();
+			attachCamera(settingsCameraCallback);
 		}
 
 		private function playTestRecording():void {
-			video.play(recName);
+			video.play(recName + ".flv");
 		}
 
 		private function startTestRecording():void {

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-flash/src/main/flex/org/apache/openmeetings/OmVideo.as
----------------------------------------------------------------------
diff --git a/openmeetings-flash/src/main/flex/org/apache/openmeetings/OmVideo.as b/openmeetings-flash/src/main/flex/org/apache/openmeetings/OmVideo.as
index cbc0ffe..e23e1ac 100644
--- a/openmeetings-flash/src/main/flex/org/apache/openmeetings/OmVideo.as
+++ b/openmeetings-flash/src/main/flex/org/apache/openmeetings/OmVideo.as
@@ -145,7 +145,7 @@ public class OmVideo {
 			}
 			videoStreamSettings.setQuality(cam.bandwidth, cam.quality);
 			videoStreamSettings.setKeyFrameInterval(cam.keyFrameInterval);
-			debug("::keyFrameInterval " + cam.keyFrameInterval);
+			debug("::camera settings ", cam.keyFrameInterval, cam.width, cam.height, cam.fps);
 			videoStreamSettings.setMode(cam.width, cam.height, cam.fps);
 			ns.videoStreamSettings = videoStreamSettings;
 		}
@@ -160,7 +160,7 @@ public class OmVideo {
 		}
 	}
 
-	private function publish(mode:String, name:String, cam:Camera, mic:Microphone, f:Function):void {
+	private function connect(callback:Function):void {
 		if (nc == null || !nc.connected) {
 			var url:String = params.url;  //TODO fallback
 			debug("NetConnection is not connected", url);
@@ -168,7 +168,7 @@ public class OmVideo {
 			nc.addEventListener(NetStatusEvent.NET_STATUS, function onConnectionStatus(e:NetStatusEvent):void {
 				debug("ConnectionStatus: " + e.info.code);
 				if (e.info.code == "NetConnection.Connect.Success") {
-					_publish(mode, name, cam, mic, f);
+					callback();
 				} else {
 					//TODO
 				}
@@ -197,27 +197,37 @@ public class OmVideo {
 				, nativeSsl: 'best' == params.proxyType
 			});
 		} else {
-			_publish(mode, name, cam, mic, f);
+			callback();
 		}
 	}
 
+	public function broadcast(name:String, cam:Camera, mic:Microphone):void {
+		connect(function():void {
+			_publish(BROADCAST, name, cam, mic, null);
+		});
+	}
+
 	public function record(name:String, cam:Camera, mic:Microphone, f:Function):void {
-		publish(RECORD, name, cam, mic, f);
+		connect(function():void {
+			_publish(RECORD, name, cam, mic, f);
+		});
 	}
 
 	public function play(name:String):void {
-		debug("PLAY::", name);
-		if (ns != null){
-			reset();
-		}
-		mode = PLAY;
-		createStream();
-		//invokes Method in baseVideoView which shows the stream
-		getVideo().attachNetStream(ns);
-		//FIXME: Commented out, cause this leads to Buffer-Full/Buffer-Empty Events
-		//after re-syncing the stream
-		//this.setBuffer(0.1);
-		ns.play(name + ".flv");
+		connect(function():void {
+			debug("PLAY::", name);
+			if (ns != null){
+				reset();
+			}
+			mode = PLAY;
+			createStream();
+			//invokes Method in baseVideoView which shows the stream
+			getVideo().attachNetStream(ns);
+			//FIXME: Commented out, cause this leads to Buffer-Full/Buffer-Empty Events
+			//after re-syncing the stream
+			//this.setBuffer(0.1);
+			ns.play(name);
+		});
 	}
 
 	public function reset():void {

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
index 0f361dd..271f119 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
@@ -137,7 +137,7 @@ public class Application extends AuthenticatedWebApplication implements IApplica
 		//chain of Resource Loaders, if not found it will search in Wicket's internal
 		//Resource Loader for a the property key
 		getResourceSettings().getStringResourceLoaders().add(0, new LabelResourceLoader());
-		getJavaScriptLibrarySettings().setJQueryReference(JQueryResourceReference.getV3());
+		getJavaScriptLibrarySettings().setJQueryReference(JQueryResourceReference.getV2());
 
 		super.init();
 

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
index 3055c86..b4da5e9 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
@@ -63,6 +63,7 @@ import org.apache.openmeetings.web.user.UserInfoDialog;
 import org.apache.openmeetings.web.user.chat.ChatPanel;
 import org.apache.openmeetings.web.user.rooms.RoomEnterBehavior;
 import org.apache.openmeetings.web.util.ContactsHelper;
+import org.apache.openmeetings.web.util.ExtendedClientProperties;
 import org.apache.openmeetings.web.util.OmUrlFragment;
 import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
@@ -127,10 +128,29 @@ public class MainPanel extends Panel {
 		super(id);
 		this.panel = _panel;
 		setOutputMarkupId(true);
-		add(topControls.setOutputMarkupPlaceholderTag(true).setMarkupId("topControls"));
 		menu = new MenuPanel("menu", getMainMenu());
 		contents = new WebMarkupContainer("contents");
-		add(contents.add(client == null ? EMPTY : _panel).setOutputMarkupId(true).setMarkupId("contents"));
+		add(chat = new ChatPanel("chatPanel"));
+		add(newMessage = new MessageDialog("newMessageDialog", new CompoundPropertyModel<>(new PrivateMessage())) {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void onClose(IPartialPageRequestHandler handler, DialogButton button) {
+				BasePanel bp = getCurrentPanel();
+				if (send.equals(button) && bp != null) {
+					bp.onNewMessageClose(handler);
+				}
+			}
+		});
+		add(userInfo = new UserInfoDialog("userInfoDialog", newMessage));
+		add(new OmAjaxClientInfoBehavior());
+	}
+
+	@Override
+	protected void onInitialize() {
+		super.onInitialize();
+		add(topControls.setOutputMarkupPlaceholderTag(true).setMarkupId("topControls"));
+		add(contents.add(client == null ? EMPTY : panel).setOutputMarkupId(true).setMarkupId("contents"));
 		topControls.add(menu.setVisible(false), topLinks.setVisible(false).setOutputMarkupPlaceholderTag(true).setMarkupId("topLinks"));
 		topLinks.add(new AjaxLink<Void>("messages") {
 			private static final long serialVersionUID = 1L;
@@ -157,24 +177,21 @@ public class MainPanel extends Panel {
 				about.open(target);
 			}
 		});
+		add(about);
 		if (getApplication().getDebugSettings().isDevelopmentUtilitiesEnabled()) {
 			add(new DebugBar("dev").setOutputMarkupId(true));
 		} else {
 			add(new EmptyPanel("dev").setVisible(false));
 		}
-		add(about, chat = new ChatPanel("chatPanel"));
-		add(newMessage = new MessageDialog("newMessageDialog", new CompoundPropertyModel<>(new PrivateMessage())) {
+		topLinks.add(new ConfirmableAjaxBorder("logout", getString("310"), getString("634")) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
-			public void onClose(IPartialPageRequestHandler handler, DialogButton button) {
-				BasePanel bp = getCurrentPanel();
-				if (send.equals(button) && bp != null) {
-					bp.onNewMessageClose(handler);
-				}
+			protected void onSubmit(AjaxRequestTarget target) {
+				getSession().invalidate();
+				setResponsePage(Application.get().getSignInPageClass());
 			}
 		});
-		add(userInfo = new UserInfoDialog("userInfoDialog", newMessage));
 		add(new AbstractDefaultAjaxBehavior() {
 			private static final long serialVersionUID = 1L;
 
@@ -224,9 +241,9 @@ public class MainPanel extends Panel {
 			@Override
 			protected void onConnect(ConnectedMessage msg) {
 				super.onConnect(msg);
+				ExtendedClientProperties cp = WebSession.get().getExtendedProperties();
 				client = new Client(getSession().getId(), msg.getKey().hashCode(), getUserId(), getBean(UserDao.class));
-				client.setRemoteAddress(WebSession.get().getClientInfo().getProperties().getRemoteAddress());
-				addOnlineUser(client);
+				addOnlineUser(cp.update(client));
 				log.debug("WebSocketBehavior::onConnect [uid: {}, session: {}, key: {}]", client.getUid(), msg.getSessionId(), msg.getKey());
 			}
 
@@ -268,21 +285,6 @@ public class MainPanel extends Panel {
 				}
 			}
 		});
-		add(new OmAjaxClientInfoBehavior());
-	}
-
-	@Override
-	protected void onInitialize() {
-		super.onInitialize();
-		topLinks.add(new ConfirmableAjaxBorder("logout", getString("310"), getString("634")) {
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			protected void onSubmit(AjaxRequestTarget target) {
-				getSession().invalidate();
-				setResponsePage(Application.get().getSignInPageClass());
-			}
-		});
 	}
 
 	private static int getLevel() {

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/HashPage.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/HashPage.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/HashPage.java
index 9ec4dea..8e4f8c6 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/HashPage.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/HashPage.java
@@ -153,9 +153,10 @@ public class HashPage extends BaseInitedPage implements IUpdatable {
 					@Override
 					protected void onClientInfo(AjaxRequestTarget target, WebClientInfo info) {
 						super.onClientInfo(target, info);
+						ExtendedClientProperties cp = (ExtendedClientProperties)info.getProperties();
 						target.appendJavaScript(
-								VideoSettings.getInitScript((ExtendedClientProperties)info.getProperties(), "hibernate", "networktest")
-								.append("VideoSettings.open();"));
+								String.format("VideoSettings.init(%s);VideoSettings.open();"
+										, VideoSettings.getInitJson(cp, "hibernate", "noclient")));
 					}
 				}));
 				error = false;

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html
index 80fae9f..87afb87 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html
@@ -42,5 +42,10 @@
 	<div id="disconnected-dlg" wicket:message="title:204, data-reload:753" style="display:none">
 		<wicket:message key="556"/>
 	</div>
+	<div style="display:none">
+		<div id="user-video">
+			<div class="video"></div>
+		</div>
+	</div>
 </wicket:panel>
 </html>

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
index c4d9728..abb98dc 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
@@ -25,6 +25,7 @@ import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.app.Application.getOnlineClient;
 import static org.apache.openmeetings.web.app.Application.getRoomClients;
 import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
+import static org.apache.openmeetings.web.app.WebSession.getSid;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
 import java.util.Calendar;
@@ -81,6 +82,7 @@ import org.apache.wicket.request.resource.ResourceReference;
 import org.red5.logging.Red5LoggerFactory;
 import org.slf4j.Logger;
 
+import com.github.openjson.JSONObject;
 import com.googlecode.wicket.jquery.core.JQueryBehavior;
 import com.googlecode.wicket.jquery.core.Options;
 import com.googlecode.wicket.jquery.ui.interaction.droppable.Droppable;
@@ -115,7 +117,10 @@ public class RoomPanel extends BasePanel {
 					, getUserId(), "0", r.getId()
 					, cp.getRemoteAddress()
 					, "" + r.getId());
-			target.appendJavaScript(VideoSettings.getInitScript(cp, "" + r.getId(), getClient().getUid()));
+			//TODO add all broadcasting clients
+			JSONObject options = VideoSettings.getInitJson(cp, "" + r.getId(), getClient().getUid());
+			options.put("interview", Room.Type.interview == r.getType());
+			target.appendJavaScript(String.format("VideoManager.init(%s);", options));
 			WebSocketHelper.sendRoom(new RoomMessage(r.getId(), getUserId(), RoomMessage.Type.roomEnter));
 			getMainPanel().getChat().roomEnter(r, target);
 			if (r.isFilesOpened()) {
@@ -354,9 +359,14 @@ public class RoomPanel extends BasePanel {
 						}
 						break;
 					case rightUpdated:
-						sidebar.update(handler);
-						menu.update(handler);
-						wb.update(handler);
+						{
+							Client c = getOnlineClient(((TextRoomMessage)m).getText());
+							handler.appendJavaScript(String.format("VideoManager.update(%s);"
+									, c.toJson().put("sid", getSid()).put("self", getClient().getUid().equals(c.getUid()))));
+							sidebar.update(handler);
+							menu.update(handler);
+							wb.update(handler);
+						}
 						break;
 					case roomEnter:
 						sidebar.update(handler);
@@ -581,7 +591,7 @@ public class RoomPanel extends BasePanel {
 	}
 
 	public void broadcast(Client client) {
-		WebSocketHelper.sendRoom(new RoomMessage(getRoom().getId(), getUserId(), RoomMessage.Type.rightUpdated));
+		WebSocketHelper.sendRoom(new TextRoomMessage(getRoom().getId(), getUserId(), RoomMessage.Type.rightUpdated, client.getUid()));
 		RoomBroadcaster.sendUpdatedClient(client);
 	}
 

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
index 98319fc..0b9a08c 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
@@ -65,12 +65,12 @@ public class VideoSettings extends Panel {
 		return String.format("%s://%s:%s/%s", protocol, host, port, app);
 	}
 
-	public static StringBuilder getInitScript(ExtendedClientProperties cp, String scope, String uid) {
+	public static JSONObject getInitJson(ExtendedClientProperties cp, String scope, String uid) {
 		JSONObject gs = getBean(ScopeApplicationAdapter.class).getFlashSettings();
 		JSONObject s = new JSONObject()
 				.put(FLASH_VIDEO_CODEC, gs.get(FLASH_VIDEO_CODEC))
 				.put(FLASH_FPS, gs.get(FLASH_FPS))
-				.put("SID", WebSession.getSid())
+				.put("sid", WebSession.getSid())
 				.put("wmode", cp.isBrowserInternetExplorer() && cp.getBrowserVersionMajor() == 11 ? "opaque" : "direct");
 		if (!Strings.isEmpty(uid)) {
 			s.put("uid", uid);
@@ -90,6 +90,6 @@ public class VideoSettings extends Panel {
 		} catch (Exception e) {
 			log.error("Error while constructing video settings parameters", e);
 		}
-		return new StringBuilder("VideoSettings.init(").append(s.toString()).append(");");
+		return s;
 	}
 }

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
index 989aa20..ffbfc79 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
@@ -16,6 +16,99 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+var Video = (function() {
+	var self = {}, c, box, v, vc, t, swf;
+
+	function _getName() {
+		return c.user.firstName + ' ' + c.user.lastName;
+	}
+	function _resetSize() {
+		v.dialog("option", "width", c.width).dialog("option", "height", t.height() + c.height + 2);
+		vc.width(c.width).height(c.height);
+		swf.attr('width', c.width).attr('height', c.height);
+	}
+	function _init(_box, _c) {
+		c = _c;
+		box = _box;
+		var _id = "video" + c.uid, name = _getName()
+			, w = c.self ? Math.max(300, c.width) : c.width
+			, h = c.self ? Math.max(200, c.height) : c.height;
+		box.append($('#user-video').clone().attr('id', _id).attr('title', name).data(self));
+		v = $('#' + _id);
+		v.dialog({
+			classes: {
+				'ui-dialog': 'ui-corner-all video user-video'
+				, 'ui-dialog-titlebar': 'ui-corner-all no-close'
+			}
+			, width: w
+			, minWidth: 40
+			, minHeight: 50
+			, autoOpen: true
+			, modal: false
+			//resizeStop
+		});
+		t = v.parent().find('.ui-dialog-titlebar').attr('title', name);
+		vc = v.find('.video');
+		vc.width(w).height(h);
+		//broadcast
+		var o = VideoManager.getOptions();
+		if (c.self) {
+			o.cam = c.cam;
+			o.mic = c.mic;
+			o.mode = 'broadcast';
+		} else {
+			o.mode = 'play';
+		}
+		o.width = c.width;
+		o.height = c.height;
+		o.uid = c.uid;
+		o.sid = c.sid;
+		o.broadcastId = c.broadcastId;
+		swf = initVideo(vc, _id + '-swf', o);
+		swf.attr('width', w).attr('height', h);
+	}
+	function _update(_c) {
+		// TODO check, update video
+		c = _c;
+	}
+
+	self.update = _update;
+	self.init = _init;
+	self.resetSize = _resetSize;
+	return self;
+});
+var VideoManager = (function() {
+	var self = {}, box, options;
+
+	function _init(_options) {
+		options = _options;
+		VideoSettings.init(self.getOptions());
+		box = $('.room.box');
+	}
+	function _getVid(uid) {
+		return "video" + uid;
+	}
+	function _update(o) {
+		var _id = _getVid(o.uid)
+			, video = o.activities.indexOf('broadcastV') > -1
+			, audio = o.activities.indexOf('broadcastA') > -1
+			, av = audio || video
+			, v = $('#' + _id);
+		if (av && v.length != 1) {
+			Video().init(box, o);
+		} else if (av && v.length == 1) {
+			v.data().update(o);
+		} else if (!av && v.length == 1) {
+			v.remove();
+		}
+	}
+
+	self.getOptions = function() { return JSON.parse(JSON.stringify(options)); };
+	self.init = _init;
+	self.update = _update;
+	self.resetSize = function(uid) { $('#' + _getVid(uid)).data().resetSize(); };
+	return self;
+})();
 function setRoomSizes() {
 	var sb = $(".room.sidebar.left")
 		, w = $(window).width() - sb.width() - 5
@@ -73,6 +166,7 @@ function roomUnload() {
 		WbArea.destroy();
 	}
 	VideoSettings.close();
+	$('.ui-dialog.user-video').remove();
 }
 function startPrivateChat(el) {
 	Chat.addTab('chatTab-u' + el.parent().parent().data("userid"), el.parent().parent().find('.user.name').text());

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/settings.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/settings.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/settings.js
index 9c96224..0a46761 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/settings.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/settings.js
@@ -41,7 +41,11 @@ var VideoSettings = (function() {
 		}
 	}
 	function _save() {
-		localStorage.setItem('openmeetings', JSON.stringify(s));
+		var _s = JSON.stringify(s);
+		localStorage.setItem('openmeetings', _s);
+		if (typeof avSettings === 'function') {
+			avSettings(_s);
+		}
 	}
 	function _init(options) {
 		vs = $('#video-settings');
@@ -85,6 +89,7 @@ var VideoSettings = (function() {
 		lm.progressbar({ value: 0 });
 		options.width = 300;
 		options.height = 200;
+		options.mode = 'settings';
 		swf = initVideo(vidScroll, 'video-settings-swf', options)[0];
 		vs.find('input, button').prop('disabled', true);
 		vs.find('button').button();

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
index ffa6ba1..62aaf2d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
@@ -27,6 +27,7 @@ import static org.apache.wicket.ajax.attributes.CallbackParameter.explicit;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.Client.Activity;
 import org.apache.openmeetings.db.entity.basic.Client.Pod;
@@ -34,6 +35,7 @@ import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder.ConfirmableBorderDialog;
 import org.apache.openmeetings.web.common.NameDialog;
@@ -41,6 +43,7 @@ import org.apache.openmeetings.web.room.RoomBroadcaster;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.openmeetings.web.room.RoomPanel.Action;
 import org.apache.openmeetings.web.room.VideoSettings;
+import org.apache.openmeetings.web.util.ExtendedClientProperties;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -212,13 +215,9 @@ public class RoomSidebar extends Panel {
 		protected void respond(AjaxRequestTarget target) {
 			StringValue s = getRequest().getRequestParameters().getParameterValue(PARAM_SETTINGS);
 			if (!s.isEmpty()) {
-				JSONObject o = new JSONObject(s.toString());
+				ExtendedClientProperties cp = WebSession.get().getExtendedProperties();
 				Client c = room.getClient();
-				c.setCam(o.optInt("cam", -1));
-				c.setMic(o.optInt("mic", -1));
-				boolean interview = Room.Type.interview == room.getRoom().getType();
-				c.setWidth(interview ? 320 : o.optInt("width"));
-				c.setHeight(interview ? 260 : o.optInt("height"));
+				cp.setSettings(new JSONObject(s.toString())).update(c, Room.Type.interview == room.getRoom().getType());
 				if (!avInited) {
 					avInited = true;
 					if (Room.Type.conference == room.getRoom().getType()) {
@@ -419,6 +418,9 @@ public class RoomSidebar extends Panel {
 				//pod has changed, no need to toggle
 				c.set(a);
 			} else {
+				if (!c.hasActivity(Activity.broadcastV) && Activity.broadcastV == a) {
+					c.setBroadcastId(ScopeApplicationAdapter.nextBroadCastId());
+				}
 				c.toggle(a);
 			}
 			room.broadcast(c);

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ExtendedClientProperties.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ExtendedClientProperties.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ExtendedClientProperties.java
index 1e339ae..a59cbbf 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ExtendedClientProperties.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ExtendedClientProperties.java
@@ -22,6 +22,8 @@ import static org.apache.openmeetings.web.app.Application.HASH_MAPPING;
 import static org.apache.openmeetings.web.app.Application.NOTINIT_MAPPING;
 import static org.apache.openmeetings.web.app.Application.SIGNIN_MAPPING;
 
+import org.apache.directory.api.util.Strings;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.wicket.protocol.http.ClientProperties;
 import org.apache.wicket.request.IRequestParameters;
 
@@ -29,6 +31,10 @@ import com.github.openjson.JSONObject;
 
 public class ExtendedClientProperties extends ClientProperties {
 	private static final long serialVersionUID = 1L;
+	public static final String CAM = "cam";
+	public static final String MIC = "mic";
+	public static final String WIDTH = "width";
+	public static final String HEIGHT = "height";
 	private String baseUrl;
 	private String codebase;
 	private String settings;
@@ -41,13 +47,14 @@ public class ExtendedClientProperties extends ClientProperties {
 		return baseUrl;
 	}
 
-	public void setSettings(JSONObject s) {
+	public ExtendedClientProperties setSettings(JSONObject s) {
 		settings = s.toString();
+		return this;
 	}
 
 	public JSONObject getSettings() {
 		try {
-			return settings == null ? new JSONObject() : new JSONObject(settings.toString());
+			return Strings.isEmpty(settings) ? new JSONObject() : new JSONObject(settings.toString());
 		} catch (Exception e) {
 			//can throw, no op
 		}
@@ -81,4 +88,20 @@ public class ExtendedClientProperties extends ClientProperties {
 		codebase = sb.append("screenshare").toString();
 		settings = parameters.getParameterValue("settings").toString("{}");
 	}
+
+	public Client update(Client c) {
+		return update(c, false);
+	}
+
+	public Client update(Client c, boolean interview) {
+		JSONObject s = getSettings().optJSONObject("video");
+		if (s == null) {
+			s = new JSONObject();
+		}
+		return c.setRemoteAddress(getRemoteAddress())
+				.setCam(s.optInt(CAM, -1))
+				.setMic(s.optInt(MIC, -1))
+				.setWidth(interview ? 320 : s.optInt(WIDTH, 0))
+				.setHeight(interview ? 260 : s.optInt(HEIGHT, 0));
+	}
 }

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/openmeetings-web/src/main/webapp/css/room.css
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/webapp/css/room.css b/openmeetings-web/src/main/webapp/css/room.css
index 36fab21..04d3a2b 100644
--- a/openmeetings-web/src/main/webapp/css/room.css
+++ b/openmeetings-web/src/main/webapp/css/room.css
@@ -352,7 +352,7 @@
 	font-weight: bold;
 	background-color: #00FF00;
 }
-.ui-dialog.video, .ui-dialog.video .ui-dialog-titlebar, .ui-dialog.video .video.ui-dialog-content {
+.ui-dialog.video, .ui-dialog.video .ui-dialog-titlebar, .ui-dialog.video .ui-dialog-content {
 	padding: 0;
 	overflow: hidden;
 }
@@ -574,3 +574,20 @@
 	border: 1px solid #888888;
 	box-shadow: 5px 5px 5px #888888;
 }
+.room.box .user-video {
+	display: inline-block !important;
+}
+.room.box .user-video .header {
+	height: 20px;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+}
+.room.box .user-video .header .ui-dialog-title {
+	padding-left: 5px;
+}
+.room.box .user-video .video {
+	min-width: 40px;
+	min-height: 30px;
+	overflow: hidden;
+}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/838bfbe3/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index bbcf269..48faea7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,7 +38,7 @@
 		<db>derby</db>
 		<junit.version>4.12</junit.version>
 		<maven.javadoc.version>2.10.3</maven.javadoc.version>
-		<maven.surefire.version>2.19.1</maven.surefire.version>
+		<maven.surefire.version>2.20</maven.surefire.version>
 		<maven-site.version>3.3</maven-site.version>
 		<wicket.version>8.0.0-SNAPSHOT</wicket.version>
 		<wicketju.version>8.0.0-M5</wicketju.version>