Merge video docking module from mconf. There are minor bugs which may be due

to using Flex 4.5
This commit is contained in:
Richard Alam 2011-09-19 16:40:18 -04:00
parent 6a5a8c45c5
commit 11e08ebde2
23 changed files with 1125 additions and 442 deletions

81
bigbluebutton-client/build.xml Normal file → Executable file
View File

@ -29,11 +29,24 @@
<property name="BREAKOUT" value="BreakoutModule" />
<property name="CLASSROOM_AUDIO" value="ClassroomAudioModule" />
<property name="SETTINGS" value="SettingsModule" />
<property name="VIDEO_DOCK" value="VideodockModule" />
<property name="AVAILABLE_LOCALES" value="az_AZ,bg_BG,cs_CZ,da_DK,de_DE,el_GR,en_US,es_ES,es_LA,fa_IR,fr_FR,fr_CA,hr_HR,hu_HU,id_ID,it_IT,ja_JP,ko_KR,lv_LV,lt_LT,nb_NO,nl_NL,pl_PL,pt_BR,pt_PT,ro_RO,ru_RU,sv_SE,sr_RS,sr_SR,th_TH,tr_TR,vi_VN,uk_UA,zh_CN,zh_TW"/>
<xmlproperty file="${SRC_DIR}/conf/locales.xml" collapseAttributes="true"/>
<target name="init-ant-contrib">
<property name="ant-contrib.jar" location="${BASE_DIR}/build/lib/ant-contrib-0.6.jar"/>
<taskdef resource="net/sf/antcontrib/antcontrib.properties" classpath="${ant-contrib.jar}"/>
</target>
<target name="localization" depends="init-ant-contrib">
<echo message="Parsing ${SRC_DIR}/conf/locales.xml for supported locales"/>
<echo message="Available locales ${AVAILABLE_LOCALES}"/>
<!--foreach list="${locales.locale.code}" target="build-locale" param="supportedlocale" delimiter=","/-->
<foreach list="${AVAILABLE_LOCALES}" target="build-locale" param="supportedlocale" delimiter=","/>
</target>
<target name="branding" depends="init-ant-contrib">
<sequential>
<mxmlc file="${SRC_DIR}/branding/css/${themeFile}" output="${OUTPUT_DIR}/branding/css/${themeFile}.swf" debug="false" mxml.compatibility-version="3.0.0" swf-version="13" optimize="true">
@ -45,6 +58,64 @@
<build-main src="${SRC_DIR}" target="${BBB_MAIN_TEST}" />
</target>
<target name="build-locale">
<echo message="Building ${supportedlocale}"/>
<available file="${LOCALE_DIR}/${supportedlocale}" type="dir" property="locale.dir.present"/>
<if>
<equals arg1="${locale.dir.present}" arg2="true"/>
<then>
<echo message="No need to copy ${LOCALE_DIR}/${supportedlocale}"/>
</then>
<else>
<echo message="Need to copy ${LOCALE_DIR}/${supportedlocale}"/>
<exec dir="${BASE_DIR}" vmlauncher="true" executable="copylocale">
<arg value="en_US"/>
<arg value="${supportedlocale}"/>
</exec>
</else>
</if>
<compileLocale locale="${supportedlocale}" />
</target>
<macrodef name="compileLocale" description="Compiles the Resource package for the given locale">
<attribute name="locale" default="en_US"/>
<sequential>
<echo message="Building @{locale}"/>
<available file="${LOCALE_DIR}/@{locale}" type="dir" property="locale.dir.present"/>
<if>
<equals arg1="${locale.dir.present}" arg2="true"/>
<then>
<echo message="No need to copy ${LOCALE_DIR}/@{locale}"/>
</then>
<else>
<echo message="Need to copy ${LOCALE_DIR}/@{locale}"/>
<exec dir="${BASE_DIR}" vmlauncher="true" executable="copylocale">
<arg value="en_US"/>
<arg value="@{locale}"/>
</exec>
</else>
</if>
<!--
Create the Flex Home directory for the language in question.
This is necessary to compensate for a bug in pre-3.2 releases of
mxmlc.
<mkdir dir="${FLEX_HOME}/frameworks/locale/@{locale}"/>-->
<!-- Invoke MXMLC -->
<mxmlc output="${OUTPUT_DIR}/locale/@{locale}_resources.swf">
<locale>@{locale}</locale>
<target-player>10.3.0</target-player>
<source-path path-element="locale/{locale}"/>
<include-resource-bundles>bbbResources</include-resource-bundles>
<include-resource-bundles>core</include-resource-bundles>
<include-resource-bundles>controls</include-resource-bundles>
<source-path path-element="${FLEX_HOME}/frameworks"/>
</mxmlc>
</sequential>
</macrodef>
<target name="build-bbb-main" description="Compile BigBlueButton Main">
<build-main src="${SRC_DIR}" target="${BBB_MAIN}" />
@ -110,7 +181,11 @@
<target name="build-video" description="Compile Video Module">
<build-module src="${SRC_DIR}" target="${VIDEO}" />
</target>
<target name="build-videodock" description="Compile Video Dock Module">
<build-module src="${SRC_DIR}" target="${VIDEO_DOCK}" />
</target>
<target name="build-whiteboard" description="Compile Whiteboard Module">
<build-module src="${SRC_DIR}" target="${WHITEBOARD}" />
</target>
@ -127,7 +202,7 @@
<!-- just a grouping of modules to compile -->
<target name="build-deskshare-phone-video-whiteboard-dyn"
depends="build-deskshare, build-phone, build-video, build-whiteboard, build-dyn, build-classroom-audio, build-settings"
depends="build-deskshare, build-phone, build-video, build-videodock, build-whiteboard, build-dyn, build-classroom-audio, build-settings"
description="Compile deskshare, phone, video, whiteboard, dynamic info modules">
</target>
@ -309,7 +384,7 @@
to be compiled withouth being optimized by using the linker -->
<target name="clean-build-bbb" depends="clean, init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone, compile-bbb"
description="Build BBB client skipping compiling of locales"/>
<target name="clean-build-all" depends="clean, init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone, compile-bbb"
<target name="clean-build-all" depends="clean, init-ant-contrib, generate-html-wrapper, localization, compile-deskshare-standalone, compile-bbb"
description="Build BBB client including locales"/>
<target name="modules" depends="init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone, compile-bbb"
description="Build BBB client without locales"/>

View File

@ -122,6 +122,9 @@ bbb.publishVideo.title = Share your webcam
bbb.publishVideo.startPublishBtn.toolTip = Start Sharing
bbb.video.publish.close.tooltip = Stop sharing your video
bbb.video.publish.close.label = Close
bbb.video.keepAspectBtn.tooltip = Keep window aspect
bbb.video.fitVideoBtn.tooltip = Fit video
bbb.video.originalSizeBtn.tooltip = Original size
# Desktop Sharing
bbb.desktopPublish.title = Desktop Sharing: Presenter's Preview
@ -183,3 +186,5 @@ bbb.settings.warning.label = Warning
bbb.settings.warning.close = Close this Warning
bbb.settings.noissues = No outstanding issues have been detected.
bbb.settings.instructions = Accept the Flash prompt that asks you for camera permissions. If you can see yourself and hear yourself, your browser has been set up correctly. Other potentials issues are shown bellow. Click on each to find a possible solution.
bbb.videodock.title = Video dock

View File

@ -94,6 +94,12 @@
dependsOn="ViewersModule"
/>
<!--<module name="VideodockModule" url="VideodockModule.swf?v=VERSION"
uri="rtmp://HOST/bigbluebutton"
dependsOn="VideoconfModule"
autoDock="true"
/>-->
<!-- new module in development:
<module name="DynamicInfoModule" url="DynamicInfoModule.swf?v=VERSION"
uri="rtmp://HOST/bigbluebutton"

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="300"
xmlns:maps="org.bigbluebutton.modules.videodock.maps.*" implements="org.bigbluebutton.common.IBigBlueButtonModule">
<mx:Script>
<![CDATA[
import org.bigbluebutton.common.LogUtil;
private var _moduleName:String = "Videodock Module";
private var _attributes:Object;
private function onCreationComplete():void {
LogUtil.debug("VideodockModule initialized");
}
public function get moduleName():String {
return _moduleName;
}
public function get autoDock():Boolean {
return (_attributes.autoDock == "true");
}
public function start(attributes:Object):void {
LogUtil.debug("Videodock attr: " + attributes.username);
_attributes = attributes;
eventMap.module = this;
eventMap.startModule();
}
public function stop():void {
eventMap.stopModule();
}
]]>
</mx:Script>
<maps:VideoDockEventMap id="eventMap"/>
</mx:Module>

View File

@ -141,5 +141,17 @@ package org.bigbluebutton.common
[Embed(source="assets/images/presenter.png")]
public var presenter:Class;
[Embed(source="assets/images/lock_open.png")]
public var lock_open:Class;
[Embed(source="assets/images/lock_close.png")]
public var lock_close:Class;
[Embed(source="assets/images/arrow_in.png")]
public var arrow_in:Class;
[Embed(source="assets/images/shape_handles.png")]
public var shape_handles:Class;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

View File

@ -36,7 +36,7 @@ package org.bigbluebutton.common.events
public static const CLOSE_WINDOW_EVENT:String = 'CLOSE_WINDOW_EVENT';
public function CloseWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
public function CloseWindowEvent(type:String=CLOSE_WINDOW_EVENT, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}

View File

@ -0,0 +1,44 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.common.events
{
import flash.events.Event;
import flash.geom.Point;
import mx.core.UIComponent;
import flexlib.mdi.containers.*;
public class DragWindowEvent extends Event
{
public static const DRAG_WINDOW_EVENT:String = "DRAG_WINDOW_EVENT";
public static const DRAG_START:String = "DRAG_START";
public static const DRAG_END:String = "DRAG_END";
public static const DRAG:String = "DRAG";
public var mouseGlobal:Point;
public var window:MDIWindow;
public var mode:String;
public function DragWindowEvent(mode:String, type:String = DRAG_WINDOW_EVENT)
{
super(type, true, false);
this.mode = mode;
}
}
}

View File

@ -313,6 +313,8 @@
<mx:dataProvider>
<mx:Array>
<mx:String>Small videos</mx:String>
<mx:String>Presenter + presentation</mx:String>
<mx:String>Meeting</mx:String>
</mx:Array>
</mx:dataProvider>
</mx:ComboBox>

View File

@ -27,7 +27,7 @@
import flexlib.mdi.containers.MDIWindow;
import mx.controls.Alert;
import mx.utils.ArrayUtil;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.LogUtil;
@ -35,8 +35,10 @@
public static const BOTTOM_LEFT:String = "BOTTOM_LEFT_WINDOW";
public static const MIDDLE:String = "MIDDLE_WINDOW";
public static const BOTTOM:String = "BOTTOM_WINDOW";
public static const RIGHT:String = "RIGHT_WINDOW";
public static const TOP_RIGHT:String = "TOP_RIGHT_WINDOW";
public static const BOTTOM_RIGHT:String = "BOTTOM_RIGHT_WINDOW";
public static const POPUP:String = "POP_UP_WINDOW";
public static const UNTOUCHED:String = "UNTOUCHED_WINDOW";
public static const DESKTOP_SHARING_VIEW:String = "POP_UP_DESKSHARE_VIEW";
public static const DESKTOP_SHARING_PUBLISH:String = "POP_UP_DESKSHARE_PUBLISH";
@ -60,7 +62,13 @@
}
public function removeWindow(window:IBbbModuleWindow):void{
windowManager.remove(window as MDIWindow);
// the flexlib windowManager remove method doesn't test if the given window is on the windows list
// this test avoid some exceptions when run the app on debugger flash player
if (ArrayUtil.getItemIndex(window, windowManager.windowList) != -1) {
windowManager.remove(window as MDIWindow);
} else {
LogUtil.debug("Trying to remove the window [ " + window + " ] but it's not a MainCanvas child");
}
}
public function removeAllWindows():void{
@ -114,12 +122,18 @@
win.width = this.width - 5;
win.height = 300;
break;
case RIGHT:
case TOP_RIGHT:
x = this.width - rightWindowWidth - 10;
y = 1;
win.width = rightWindowWidth;
win.height = rightWindowHeight;
break;
case BOTTOM_RIGHT:
x = this.width - rightWindowWidth - 10;
y = rightWindowHeight + 10;;
win.width = rightWindowWidth;
win.height = this.height - rightWindowHeight - 10;
break;
case POPUP:
x = (Math.random() * this.width) - 640;
y = (Math.random() * this.height) - 520;
@ -134,6 +148,10 @@
x = 1;
y = 1;
break;
case UNTOUCHED:
// don't reposition the window
x = win.x;
y = win.y;
}
windowManager.absPos(win, x, y);
}

View File

@ -53,7 +53,7 @@
[Bindable] public var chatOptions:ChatOptions;
public function getPrefferedPosition():String{
return MainCanvas.RIGHT;
return MainCanvas.TOP_RIGHT;
}
private function onCreationComplete():void {

View File

@ -0,0 +1,55 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.videoconf.business
{
import mx.containers.HBox;
import mx.controls.Button;
import flash.utils.Dictionary;
import flash.events.MouseEvent;
import org.bigbluebutton.common.LogUtil;
public class ButtonsOverlay extends HBox
{
private var buttons:Dictionary = new Dictionary;
private var BUTTONS_SIZE:int = 20;
private var BUTTONS_PADDING:int = 10;
public function add(name:String, icon:Class, tooltip:String, listener:Function):void {
var button:Button = new Button;
button.setStyle("icon", icon);
button.toolTip = tooltip;
button.addEventListener(MouseEvent.CLICK, listener);
button.width = button.height = BUTTONS_SIZE;
this.addChild(button);
buttons[name] = button;
}
public function get(name:String):Button {
var tmp:Object = buttons[name];
//return (flash.utils.getQualifiedClassName(tmp) == "mx.controls::Button"? (tmp as Button): null);
return (tmp as Button);
}
public function get padding():int {
return BUTTONS_PADDING;
}
}
}

View File

@ -0,0 +1,322 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.videoconf.business
{
import flexlib.mdi.containers.MDIWindow;
import flexlib.mdi.events.MDIWindowEvent;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.events.DragWindowEvent;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.util.i18n.ResourceUtil;
import mx.core.UIComponent;
import mx.controls.Button;
import flash.events.MouseEvent;
import flash.media.Video;
import flash.geom.Point;
public class VideoWindowItf extends MDIWindow implements IBbbModuleWindow
{
protected var _video:Video;
protected var _videoHolder:UIComponent;
// images must be static because it needs to be created *before* the PublishWindow creation
static protected var images:Images = new Images();
static public var PADDING_HORIZONTAL:Number = 6;
static public var PADDING_VERTICAL:Number = 29;
protected var _minWidth:int = 160 + PADDING_HORIZONTAL;
protected var _minHeight:int = 120 + PADDING_VERTICAL;
protected var aspectRatio:Number = 1;
protected var keepAspect:Boolean = false;
protected var originalWidth:Number;
protected var originalHeight:Number;
protected var mousePositionOnDragStart:Point;
public var streamName:String;
public var userId:int;
[Bindable] public var resolutions:Array;
protected function getVideoResolution(stream:String):Array {
for each (var resStr:String in resolutions){
if (resStr == stream.substr(0, resStr.length))
return resStr.split( "x" );
}
return null;
}
protected function get paddingVertical():Number {
return this.borderMetrics.top + this.borderMetrics.bottom;
}
protected function get paddingHorizontal():Number {
return this.borderMetrics.left + this.borderMetrics.right;
}
static private var RESIZING_DIRECTION_UNKNOWN:int = 0;
static private var RESIZING_DIRECTION_VERTICAL:int = 1;
static private var RESIZING_DIRECTION_HORIZONTAL:int = 2;
static private var RESIZING_DIRECTION_BOTH:int = 3;
private var resizeDirection:int = RESIZING_DIRECTION_BOTH;
/**
* when the window is resized by the user, the application doesn't know
* about the resize direction
*/
public function onResizeStart(event:MDIWindowEvent = null):void {
resizeDirection = RESIZING_DIRECTION_UNKNOWN;
}
/**
* after the resize ends, the direction is set to BOTH because of the
* non-user resize actions - like when the window is docked, and so on
*/
public function onResizeEnd(event:MDIWindowEvent = null):void {
resizeDirection = RESIZING_DIRECTION_BOTH;
}
protected function onResize():void {
if (_video == null || _videoHolder == null || this.minimized) return;
// limits the window size to the parent size
this.width = (this.parent != null? Math.min(this.width, this.parent.width): this.width);
this.height = (this.parent != null? Math.min(this.height, this.parent.height): this.height);
var tmpWidth:Number = this.width - PADDING_HORIZONTAL;
var tmpHeight:Number = this.height - PADDING_VERTICAL;
// try to discover in which direction the user is resizing the window
if (resizeDirection != RESIZING_DIRECTION_BOTH) {
if (tmpWidth == _video.width && tmpHeight != _video.height)
resizeDirection = (resizeDirection == RESIZING_DIRECTION_VERTICAL || resizeDirection == RESIZING_DIRECTION_UNKNOWN? RESIZING_DIRECTION_VERTICAL: RESIZING_DIRECTION_BOTH);
else if (tmpWidth != _video.width && tmpHeight == _video.height)
resizeDirection = (resizeDirection == RESIZING_DIRECTION_HORIZONTAL || resizeDirection == RESIZING_DIRECTION_UNKNOWN? RESIZING_DIRECTION_HORIZONTAL: RESIZING_DIRECTION_BOTH);
else
resizeDirection = RESIZING_DIRECTION_BOTH;
}
// depending on the direction, the tmp size is different
switch (resizeDirection) {
case RESIZING_DIRECTION_VERTICAL:
tmpWidth = Math.floor(tmpHeight * aspectRatio);
break;
case RESIZING_DIRECTION_HORIZONTAL:
tmpHeight = Math.floor(tmpWidth / aspectRatio);
break;
case RESIZING_DIRECTION_BOTH:
// this direction is used also for non-user window resize actions
tmpWidth = Math.min (tmpWidth, Math.floor(tmpHeight * aspectRatio));
tmpHeight = Math.min (tmpHeight, Math.floor(tmpWidth / aspectRatio));
break;
}
_video.width = _videoHolder.width = tmpWidth;
_video.height = _videoHolder.height = tmpHeight;
if (!keepAspect || this.maximized) {
// center the video in the window
_video.x = Math.floor ((this.width - PADDING_HORIZONTAL - tmpWidth) / 2);
_video.y = Math.floor ((this.height - PADDING_VERTICAL - tmpHeight) / 2);
} else {
// fit window dimensions on video
_video.x = 0;
_video.y = 0;
this.width = tmpWidth + PADDING_HORIZONTAL;
this.height = tmpHeight + PADDING_VERTICAL;
}
// reposition the window to fit inside the parent window
if (this.parent != null) {
if (this.x + this.width > this.parent.width)
this.x = this.parent.width - this.width;
if (this.x < 0)
this.x = 0;
if (this.y + this.height > this.parent.height)
this.y = this.parent.height - this.height;
if (this.y < 0)
this.y = 0;
}
updateButtonsPosition();
}
public function updateWidth():void {
this.width = Math.floor((this.height - paddingVertical) * aspectRatio) + paddingHorizontal;
onResize();
}
public function updateHeight():void {
this.height = Math.floor((this.width - paddingHorizontal) / aspectRatio) + paddingVertical;
onResize();
}
protected function setAspectRatio(width:int,height:int):void {
aspectRatio = (width/height);
this.minHeight = Math.floor((this.minWidth - PADDING_HORIZONTAL) / aspectRatio) + PADDING_VERTICAL;
}
public function getPrefferedPosition():String{
if (_buttonsEnabled)
return MainCanvas.POPUP;
else
// the window is docked, so it should not be moved on reset layout
return MainCanvas.UNTOUCHED;
}
public function onDrag(event:MDIWindowEvent = null):void {
var e:DragWindowEvent = new DragWindowEvent(DragWindowEvent.DRAG);
e.mouseGlobal = this.localToGlobal(new Point(mouseX, mouseY));
e.window = this;
dispatchEvent(e);
}
public function onDragStart(event:MDIWindowEvent = null):void {
var e:DragWindowEvent = new DragWindowEvent(DragWindowEvent.DRAG_START);
e.window = this;
dispatchEvent(e);
}
public function onDragEnd(event:MDIWindowEvent = null):void {
var e:DragWindowEvent = new DragWindowEvent(DragWindowEvent.DRAG_END);
e.mouseGlobal = this.localToGlobal(new Point(mouseX, mouseY));
e.window = this;
dispatchEvent(e);
}
override public function close(event:MouseEvent = null):void{
var e:CloseWindowEvent = new CloseWindowEvent();
e.window = this;
dispatchEvent(e);
super.close(event);
}
private var _buttons:ButtonsOverlay = null;
private var _buttonsEnabled:Boolean = true;
private var img_unlock_keep_aspect:Class = images.lock_open;
private var img_lock_keep_aspect:Class = images.lock_close;
private var img_fit_video:Class = images.arrow_in;
private var img_original_size:Class = images.shape_handles;
protected function get buttons():ButtonsOverlay {
if (_buttons == null) {
_buttons = new ButtonsOverlay;
_buttons.add("originalSizeBtn", img_original_size, ResourceUtil.getInstance().getString('bbb.video.originalSizeBtn.tooltip'), onOriginalSizeClick);
// hiding the other buttons
//_buttons.add("keepAspectBtn", img_lock_keep_aspect, ResourceUtil.getInstance().getString('bbb.video.keepAspectBtn.tooltip'), onKeepAspectClick);
//_buttons.add("fitVideoBtn", img_fit_video, ResourceUtil.getInstance().getString('bbb.video.fitVideoBtn.tooltip'), onFitVideoClick);
_buttons.visible = false;
this.addChild(_buttons);
}
return _buttons;
}
protected function createButtons():void {
// creates the window keeping the aspect ratio
onKeepAspectClick();
}
protected function updateButtonsPosition():void {
if (buttons.visible == false) {
buttons.y = buttons.x = 0;
} else {
buttons.y = _video.y + _video.height - buttons.height - buttons.padding;
buttons.x = _video.x + _video.width - buttons.width - buttons.padding;
}
}
protected function showButtons(event:MouseEvent = null):void {
if (_buttonsEnabled && buttons.visible == false) {
buttons.visible = true;
updateButtonsPosition();
}
}
protected function hideButtons(event:MouseEvent = null):void {
if (_buttonsEnabled && buttons.visible == true) {
buttons.visible = false;
updateButtonsPosition();
}
}
protected function onDoubleClick(event:MouseEvent = null):void {
// it occurs when the window is docked, for example
if (!this.maximizeRestoreBtn.visible) return;
this.maximizeRestore();
}
override public function maximizeRestore(event:MouseEvent = null):void {
// if the user is maximizing the window, the control buttons should disappear
buttonsEnabled = this.maximized;
super.maximizeRestore(event);
}
public function set buttonsEnabled(enabled:Boolean):void {
if (!enabled)
hideButtons();
_buttonsEnabled = enabled;
}
protected function onOriginalSizeClick(event:MouseEvent = null):void {
_video.width = _videoHolder.width = originalWidth;
_video.height = _videoHolder.height = originalHeight;
onFitVideoClick();
}
protected function onFitVideoClick(event:MouseEvent = null):void {
var newWidth:int = _video.width + paddingHorizontal;
var newHeight:int = _video.height + paddingVertical;
this.x += (this.width - newWidth)/2;
this.y += (this.height - newHeight)/2;
this.width = newWidth;
this.height = newHeight;
onResize();
}
protected function onKeepAspectClick(event:MouseEvent = null):void {
keepAspect = !keepAspect;
var keepAspectBtn:Button = buttons.get("keepAspectBtn");
if (keepAspectBtn != null) {
keepAspectBtn.selected = keepAspect;
keepAspectBtn.setStyle("icon", (keepAspect? img_lock_keep_aspect: img_unlock_keep_aspect));
}
var fitVideoBtn:Button = buttons.get("fitVideoBtn");
if (fitVideoBtn != null) {
fitVideoBtn.enabled = !keepAspect;
}
onFitVideoClick();
}
}
}

View File

@ -0,0 +1,44 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.videoconf.events
{
import flash.events.Event;
import org.bigbluebutton.common.IBbbModuleWindow;
/**
* Dispatch this event with your IBbbModuleWindow instance attached to add the MDIWindow to the main canvas area of bbb-client.
*
*/
public class OpenVideoWindowEvent extends Event
{
/**
* The MDIWindow instance to show on the main canvas
*/
public var window:IBbbModuleWindow;
public static const OPEN_VIDEO_WINDOW_EVENT:String = 'OPEN_VIDEO_WINDOW_EVENT';
public function OpenVideoWindowEvent(type:String=OPEN_VIDEO_WINDOW_EVENT, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}

View File

@ -39,6 +39,7 @@
import org.bigbluebutton.modules.videoconf.business.VideoProxy;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.events.OpenPublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.modules.videoconf.views.PublishWindow;
@ -70,6 +71,11 @@
var windowEvent:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
windowEvent.window = window;
globalDispatcher.dispatchEvent(windowEvent);
// this event will dock the window, if it's enabled
var openVideoEvent:OpenVideoWindowEvent = new OpenVideoWindowEvent();
openVideoEvent.window = window;
globalDispatcher.dispatchEvent(openVideoEvent);
}
private function viewVideoFile(e:BBBEvent):void {
@ -93,7 +99,7 @@
private function openPublishWindow():void{
publishWindow = new PublishWindow();
publishWindow.videoOptions = proxy.videoOptions;
publishWindow.streamName = "-" + module.userid.toString();
publishWindow.userId = module.userid;
publishWindow.userrole = module.role;
publishWindow.quality = module.quality;
publishWindow.resolutions = module.resolutions.split(",");
@ -110,6 +116,7 @@
broadcastEvent.stream = e.stream;
broadcastEvent.userid = module.userid;
globalDispatcher.dispatchEvent(broadcastEvent);
publishWindow.title = module.username + " (you)";
}
private function stopPublishing(e:StopBroadcastEvent):void{

View File

@ -20,20 +20,21 @@
$Id: $
-->
<pubVid:MDIWindow xmlns="flexlib.mdi.containers.*"
<pubVid:VideoWindowItf xmlns="org.bigbluebutton.modules.videoconf.business.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:pubVid="flexlib.mdi.containers.*"
xmlns:pubVid="org.bigbluebutton.modules.videoconf.business.*"
implements="org.bigbluebutton.common.IBbbModuleWindow"
creationComplete="init()"
width="{camWidth + 6}" height="{camHeight + 80}"
title="{ResourceUtil.getInstance().getString('bbb.publishVideo.title')}"
xmlns:mate="http://mate.asfusion.com/"
resize="onResize()">
resize="onResize()"
layout="absolute">
<mx:Script>
<![CDATA[
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
@ -43,34 +44,20 @@
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
private var images:Images = new Images();
[Bindable] public var camIcon:Class = images.control_play;
[Bindable] public var bbbLogo:Class = images.bbb_logo;
[Bindable] public var resolutions:Array = new Array("320x240", "640x480");
private var video:Video;
public var streamName:String;
[Bindable] private var camWidth:Number = 320;
[Bindable] private var camHeight:Number = 240;
private var _userrole:String;
public var quality:Number = 0;
private var _bResizePossible:Boolean = true;
private var _nAspectRatio:Number = 1;
static private var _minWidth:int = 86;
static private var _minHeight:int = 174;
private var _nSavedVideoWidth:Number = 0;
private var _nSavedVideoHeight:Number = 0;
private var _nOldWindowWidth:Number = 0;
private var _nOldWindowHeight:Number = 0;
// Timer to auto-publish webcam. We need this timer to delay
// the auto-publishing until after the Viewers's window has loaded
// to receive the publishing events. Otherwise, the user joining next
@ -81,14 +68,17 @@
public var videoOptions:VideoConfOptions;
private function init():void{
_videoHolder = new UIComponent();
_videoHolder.width = camWidth;
_videoHolder.height = camHeight;
this.addChild(_videoHolder);
this.title = ResourceUtil.getInstance().getString('bbb.publishVideo.title');
currentState = "dispVideoOptionsControlBar";
checkIfMacCamera();
if (isPresenter()) showResControls(true);
if (Camera.names.length > 1) showVideoControls(true);
if (!isPresenter() && Camera.names.length == 1) startPublishing();
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEvent);
addEventListener(MDIWindowEvent.RESIZE, onResizeEvent);
addEventListener(MDIWindowEvent.MAXIMIZE, onMaximize);
addEventListener(MDIWindowEvent.RESTORE, onRestore);
this.minWidth = _minWidth;
this.minHeight = _minHeight;
@ -121,11 +111,7 @@
if (Camera.names[i] == webcam) cmbCameraSelector.selectedIndex = i;
}
}
public function getPrefferedPosition():String{
return MainCanvas.POPUP;
}
private function startPublishing():void{
var camera:Camera = Camera.getCamera(cmbCameraSelector.selectedIndex.toString());
if (camera == null) return;
@ -136,13 +122,13 @@
camera.setMode(camWidth, camHeight, videoOptions.camModeFps);
camera.setQuality(videoOptions.camQualityBandwidth, videoOptions.camQualityPicture);
video = new Video(camWidth, camHeight);
_video = new Video(camWidth, camHeight);
//Next two lines may seem redundant but they're not. Do not delete.
video.width = camWidth;
video.height = camHeight;
adjustWindowSize();
video.attachCamera(camera);
videoHolder.addChild(video);
_video.width = camWidth;
_video.height = camHeight;
onResize();
_video.attachCamera(camera);
_videoHolder.addChild(_video);
// addChild(videoHolder);
var e:StartBroadcastEvent = new StartBroadcastEvent();
@ -155,6 +141,22 @@
maximizeRestoreBtn.visible = true;
this.resizable = true;
addEventListener(MDIWindowEvent.RESIZE_START, onResizeStart);
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEnd);
addEventListener(MDIWindowEvent.DRAG_START, onDragStart);
addEventListener(MDIWindowEvent.DRAG, onDrag);
addEventListener(MDIWindowEvent.DRAG_END, onDragEnd);
addEventListener(MouseEvent.MOUSE_OVER, showButtons);
addEventListener(MouseEvent.MOUSE_OUT, hideButtons);
addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
createButtons();
// this event will dock the window, if it's enabled
var openVideoEvent:OpenVideoWindowEvent = new OpenVideoWindowEvent();
openVideoEvent.window = this;
dispatchEvent(openVideoEvent);
}
override public function close(event:MouseEvent=null):void{
@ -163,28 +165,28 @@
}
private function stopPublishing():void{
if (video != null) {
video.attachCamera(null);
video.clear();
video = null;
if (_video != null) {
_video.attachCamera(null);
_video.clear();
_video = null;
}
var e:StopBroadcastEvent = new StopBroadcastEvent()
e.stream = this.streamName;
e.stream = streamName;
dispatchEvent(e);
}
private function setResolution():void{
var res:Array = cmbResolution.selectedLabel.split( "x" );
camWidth = Number(res[0]);
camHeight = Number(res[1]);
_nAspectRatio = (camWidth/camHeight);
camWidth = originalWidth = Number(res[0]);
camHeight = originalHeight = Number(res[1]);
setAspectRatio(camWidth, camHeight);
/**
* Add timestamp to create a unique stream name. This way we can record
* stream without overwriting previously recorded streams.
*/
var curTime:Number = new Date().getTime();
this.streamName = cmbResolution.selectedLabel.concat(this.streamName) + "-" + curTime;
this.streamName = cmbResolution.selectedLabel.concat(this.userId) + "-" + curTime;
}
public function set userrole(role:String):void{
@ -198,6 +200,7 @@
private function showVideoControls(show:Boolean):void{
if (show){
currentState = "dispVideoOptionsControlBar"
videoOptionsBar.visible = true;
btnStartPublish.visible = true;
cmbCameraSelector.visible = true;
@ -206,6 +209,7 @@
btnStartPublish.visible = false;
cmbCameraSelector.visible = false;
videoOptionsBar.visible = false;
currentState = "dispVideo";
}
}
@ -233,146 +237,27 @@
}
}
private function adjustWindowSize():void{
videoHolder.width = video.width;
videoHolder.height = video.height;
this.width = video.width + 6;
this.height = video.height + 74;
// prevent to show a video window bigger than the parent window
if (this.width > this.parent.width) {
video.width = this.parent.width - 6;
video.height = Math.floor(video.width / _nAspectRatio);
adjustWindowSize();
}
if (this.height > this.parent.height) {
video.height = this.parent.height - 74;
video.width = Math.floor(video.height * _nAspectRatio);
adjustWindowSize();
}
if (this.width < _minWidth) {
video.width = _minWidth - 6;
video.height = Math.floor(video.width / _nAspectRatio);
adjustWindowSize();
}
if (this.height < _minHeight) {
video.height = _minHeight - 74;
video.width = Math.floor(video.height * _nAspectRatio);
adjustWindowSize();
}
}
public function onResizeEvent(event:MDIWindowEvent):void {
if (event.type == MDIWindowEvent.RESIZE) {
// test if we are already resizing
if (_bResizePossible) {
_bResizePossible = false;
resizeWindow();
_bResizePossible = true;
}
} else if (event.type == MDIWindowEvent.RESIZE_END) {
adjustWindowSize();
}
}
private function resizeWindow():void {
// prevent the window for blinking
if (this.width == _nOldWindowWidth && this.height == _nOldWindowHeight) {
adjustWindowSize();
return;
}
_nOldWindowWidth = this.width;
_nOldWindowHeight = this.height;
if (this.width == video.width + 6) {
// if it's a vertical resizing
video.height = this.height - 74;
video.width = video.height * _nAspectRatio;
} else {
// if it's a horizontal resizing
video.width = this.width - 6;
video.height = Math.floor (video.width / _nAspectRatio);
}
adjustWindowSize();
}
public function onMaximize(event:MDIWindowEvent):void {
_nSavedVideoWidth = video.width;
_nSavedVideoHeight = video.height;
var tmpWidth:Number = this.parent.width - 6;
var tmpHeight:Number = this.parent.height - 74;
if (tmpWidth > tmpHeight * _nAspectRatio)
tmpWidth = tmpHeight * _nAspectRatio;
if (tmpHeight > Math.floor(tmpWidth / _nAspectRatio))
tmpHeight = Math.floor(tmpWidth / _nAspectRatio);
video.width = tmpWidth;
video.height = tmpHeight;
video.x = Math.floor ((this.parent.width - 6 - video.width) / 2);
video.y = Math.floor ((this.parent.height - 74 - video.height) / 2);
}
public function onRestore(event:MDIWindowEvent):void {
video.x = 0;
video.y = 0;
video.width = _nSavedVideoWidth;
video.height = _nSavedVideoHeight;
adjustWindowSize();
}
public function setWindowWidth(width:int):void {
if (video == null) return;
video.width = width - 6;
video.height = Math.floor(video.width / _nAspectRatio);
adjustWindowSize();
}
static public function getMinWidth():int {
return _minWidth;
}
static public function getMinHeight():int {
return _minHeight;
}
static public function getWindowHeightByWidth(width:int, aspect:Number):int {
return Math.floor((width - 6) / aspect) + 74;
}
static public function getWindowWidthByHeight(height:int, aspect:Number):int {
return Math.floor((height - 74) * aspect) + 6;
}
override protected function resourcesChanged():void{
super.resourcesChanged();
this.title = ResourceUtil.getInstance().getString('bbb.publishVideo.title');
}
private function localeChanged(e:LocaleChangeEvent):void{
resourcesChanged();
}
private function onResize():void{
setWindowWidth(this.width);
}
// when the user starts to publish, the window title is your name + (you)
if (currentState == "dispVideoOptionsControlBar")
this.title = ResourceUtil.getInstance().getString('bbb.publishVideo.title');
}
]]>
</mx:Script>
<pubVid:states>
<mx:State name="dispVideo"/>
<mx:State name="dispVideoOptionsControlBar">
<mx:AddChild>
<mx:ControlBar id="videoOptionsBar" visible="true">
<mx:Button id="btnStartPublish" toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.toolTip')}" icon="{camIcon}" click="startPublishing()" />
<mx:ComboBox id="cmbCameraSelector" dataProvider="{Camera.names}" width="150" visible="false" />
<mx:ComboBox id="cmbResolution" dataProvider="{resolutions}" width="20%" visible="false" />
</mx:ControlBar>
</mx:AddChild>
</mx:State>
</pubVid:states>
<mx:UIComponent id="videoHolder" width="{camWidth}" height="{camHeight}" />
<mx:ControlBar id="videoOptionsBar" visible="true">
<mx:Button id="btnStartPublish" toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.toolTip')}" icon="{camIcon}" click="startPublishing()" />
<mx:ComboBox id="cmbCameraSelector" dataProvider="{Camera.names}" width="150" visible="false" />
<mx:ComboBox id="cmbResolution" dataProvider="{resolutions}" width="20%" visible="false" />
</mx:ControlBar>
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
</pubVid:MDIWindow>
</pubVid:VideoWindowItf>

View File

@ -20,66 +20,50 @@
$Id: $
-->
<MDIWindow xmlns="flexlib.mdi.containers.*"
<VideoWindowItf xmlns="org.bigbluebutton.modules.videoconf.business.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="init()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
resize="onResize()">
resize="onResize()"
layout="absolute">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.core.UIComponent;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
public var resolutions:Array = new Array("320x240", "640x480");
private var video:Video;
private var ns:NetStream;
private var videoHolder:UIComponent;
private var stream:String;
private var videoHeight:Number;
private var videoWidth:Number;
private var _bResizePossible:Boolean = true;
private var _nAspectRatio:Number = 1;
static private var _minWidth:int = 166;
static private var _minHeight:int = 149;
private var _nSavedVideoWidth:Number = 0;
private var _nSavedVideoHeight:Number = 0;
private var _nOldWindowWidth:Number = 0;
private var _nOldWindowHeight:Number = 0;
private var _xPosition:int;
private var _yPosition:int;
private var ns:NetStream;
private var globalDispatcher:Dispatcher;
[Bindable]
public var videoOptions:VideoConfOptions;
private function init():void{
videoHolder = new UIComponent();
videoHolder.addChild(video);
this.addChild(videoHolder);
_videoHolder = new UIComponent();
_videoHolder.addChild(_video);
this.addChild(_videoHolder);
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEvent);
addEventListener(MDIWindowEvent.RESIZE, onResizeEvent);
addEventListener(MDIWindowEvent.MAXIMIZE, onMaximize);
addEventListener(MDIWindowEvent.RESTORE, onRestore);
addEventListener(MDIWindowEvent.RESIZE_START, onResizeStart);
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEnd);
addEventListener(MDIWindowEvent.CLOSE, onCloseEvent);
addEventListener(MDIWindowEvent.DRAG_START, onDragStart);
addEventListener(MDIWindowEvent.DRAG, onDrag);
addEventListener(MDIWindowEvent.DRAG_END, onDragEnd);
addEventListener(MouseEvent.MOUSE_OVER, showButtons);
addEventListener(MouseEvent.MOUSE_OUT, hideButtons);
addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
createButtons();
globalDispatcher = new Dispatcher();
@ -87,14 +71,24 @@
this.minHeight = _minHeight;
maximizeRestoreBtn.visible = true;
this.resizable = true;
/**
* At this point, the function startVideo has been called, and
* the video has the exactly dimensions of the original stream.
* It's needed to call onResize() to fit the video window in the
* main canvas in case that the video dimensions are larger than
* the parent window.
*/
onResize();
if (videoOptions.viewerWindowMaxed)
this.maximize();
}
private function onCloseEvent(event:MDIWindowEvent = null):void {
LogUtil.debug("ViewWindow closing " + stream);
LogUtil.debug("ViewWindow closing " + streamName);
var bbbEvt:BBBEvent = new BBBEvent("ViewVideoCloseEvent");
bbbEvt.message = stream;
bbbEvt.message = streamName;
dispatchEvent(bbbEvt);
}
@ -107,58 +101,43 @@
ns.receiveVideo(true);
ns.receiveAudio(false);
setVideoResolution(stream);
video = new Video(this.width, this.height);
video.width = this.width;
video.height = this.height;
video.attachNetStream(ns);
ns.play(stream);
this.stream = stream;
this.width = video.width + 6;
this.height = video.height + 29;
var res:Array = getVideoResolution(stream);
if (res == null) // error
return;
_video = new Video(Number(res[0]), Number(res[1]));
_video.width = originalWidth = Number(res[0]);
_video.height = originalHeight = Number(res[1]);
setAspectRatio(Number(res[0]), Number(res[1]));
_video.attachNetStream(ns);
ns.play(stream);
this.streamName = stream;
this.width = _video.width + paddingHorizontal;
this.height = _video.height + paddingVertical;
}
private function setVideoResolution(stream:String):void{
for each (var resStr:String in resolutions){
LogUtil.debug("VideoWindow::setVideoResolution testing " + resStr);
if (resStr == stream.substr(0, resStr.length)) {
var res:Array = resStr.split( "x" );
this.width = Number(res[0]);
this.height = Number(res[1]);
LogUtil.debug("VideoWindow::setVideoResolution width=" + this.width + " height=" + this.height);
_nAspectRatio = (this.width/this.height);
break;
}
}
}
private function onAsyncError(e:AsyncErrorEvent):void{
LogUtil.debug("VideoWindow::asyncerror " + e.toString());
}
public function onMetaData(info:Object):void{
LogUtil.debug("metadata: width=" + info.width + " height=" + info.height);
video.width = info.width;
video.height = info.height;
adjustWindowSize();
_video.width = info.width;
_video.height = info.height;
setAspectRatio(info.width, info.height);
onResize();
}
public function getPrefferedPosition():String {
if (videoOptions.viewerWindowLocation == "MIDDLE") {
return MainCanvas.MIDDLE;
}
return MainCanvas.POPUP;
}
private function onNetStatus(e:NetStatusEvent):void{
switch(e.info.code){
case "NetStream.Publish.Start":
LogUtil.debug("NetStream.Publish.Start for broadcast stream " + stream);
LogUtil.debug("NetStream.Publish.Start for broadcast stream " + streamName);
break;
case "NetStream.Play.UnpublishNotify":
ns.close();
this.close();
// shouldn't call onCloseEvent() here because of the viewer cam icon
super.close();
break;
case "NetStream.Play.Start":
LogUtil.debug("Netstatus: " + e.info.code);
@ -175,131 +154,16 @@
override public function close(event:MouseEvent=null):void{
ns.close();
//onCloseEvent();
onCloseEvent();
super.close(event);
}
private function closeWindow(e:CloseAllWindowsEvent):void{
this.close();
}
private function adjustWindowSize():void{
if (videoHolder == null) return;
videoHolder.width = video.width;
videoHolder.height = video.height;
this.width = video.width + 6;
this.height = video.height + 29;
// prevent to show a video window bigger than the parent window
if (this.width > this.parent.width) {
video.width = this.parent.width - 6;
video.height = Math.floor(video.width / _nAspectRatio);
adjustWindowSize();
}
if (this.height > this.parent.height) {
video.height = this.parent.height - 29;
video.width = Math.floor(video.height * _nAspectRatio);
adjustWindowSize();
}
if (this.width < _minWidth) {
video.width = _minWidth - 6;
video.height = Math.floor(video.width / _nAspectRatio);
adjustWindowSize();
}
if (this.height < _minHeight) {
video.height = _minHeight - 29;
video.width = Math.floor(video.height * _nAspectRatio);
adjustWindowSize();
}
}
public function onResizeEvent(event:MDIWindowEvent):void {
if (event.type == MDIWindowEvent.RESIZE) {
// test if we are already resizing
if (_bResizePossible) {
_bResizePossible = false;
resizeWindow();
_bResizePossible = true;
}
} else if (event.type == MDIWindowEvent.RESIZE_END) {
adjustWindowSize();
}
}
private function resizeWindow():void {
// prevent the window for blinking
if (this.width == _nOldWindowWidth && this.height == _nOldWindowHeight) {
adjustWindowSize();
return;
}
_nOldWindowWidth = this.width;
_nOldWindowHeight = this.height;
if (this.width == video.width + 6) {
// if it's a vertical resizing
video.height = this.height - 29;
video.width = video.height * _nAspectRatio;
} else {
// if it's a horizontal resizing
video.width = this.width - 6;
video.height = Math.floor (video.width / _nAspectRatio);
}
adjustWindowSize();
}
public function onMaximize(event:MDIWindowEvent):void {
_nSavedVideoWidth = video.width;
_nSavedVideoHeight = video.height;
var tmpWidth:Number = this.parent.width - 6;
var tmpHeight:Number = this.parent.height - 29;
if (tmpWidth > tmpHeight * _nAspectRatio)
tmpWidth = tmpHeight * _nAspectRatio;
if (tmpHeight > Math.floor(tmpWidth / _nAspectRatio))
tmpHeight = Math.floor(tmpWidth / _nAspectRatio);
video.width = tmpWidth;
video.height = tmpHeight;
video.x = Math.floor ((this.parent.width - 6 - video.width) / 2);
video.y = Math.floor ((this.parent.height - 29 - video.height) / 2);
}
public function onRestore(event:MDIWindowEvent):void {
video.x = 0;
video.y = 0;
video.width = _nSavedVideoWidth;
video.height = _nSavedVideoHeight;
adjustWindowSize();
}
public function setWindowWidth(width:int):void {
if (video == null) return;
video.width = width - 6;
video.height = Math.floor(video.width / _nAspectRatio);
adjustWindowSize();
}
static public function getMinWidth():int {
return _minWidth;
}
static public function getMinHeight():int {
return _minHeight;
}
private function onResize():void{
setWindowWidth(this.width);
}
]]>
</mx:Script>
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
</MDIWindow>
</VideoWindowItf>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org
Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
BigBlueButton is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2.1 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$Id: $
-->
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.modules.videodock.views.VideoDock;
public var module:VideodockModule;
private var videoDock:VideoDock;
public function startModule():void{
videoDock = new VideoDock();
videoDock.autoDock = module.autoDock;
var windowEvent:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
windowEvent.window = videoDock;
globalDispatcher.dispatchEvent(windowEvent);
}
public function stopModule():void {
videoDock.removeAllChildren();
}
]]>
</mx:Script>
</EventMap>

View File

@ -0,0 +1,265 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org
Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
BigBlueButton is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2.1 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$Id: $
-->
<MDIWindow xmlns="flexlib.mdi.containers.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="init()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
title="{ResourceUtil.getInstance().getString('bbb.videodock.title')}"
xmlns:mate="http://mate.asfusion.com/"
layout="absolute"
horizontalAlign="center"
verticalAlign="middle"
resize="onChildAdd()"
backgroundColor="0xDDDDDD">
<mx:Script>
<![CDATA[
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.DragWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.business.VideoWindowItf;
import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
import mx.events.ChildExistenceChangedEvent;
public var autoDock:Boolean = false;
private var childrenDimension:Dictionary = new Dictionary();
private var borderColor:String;
private function init():void{
this.showCloseButton = false;
this.minWidth = 172;
this.minHeight = 179;
this.maxWidth = this.parent.width;
this.maxHeight = this.parent.height;
this.width = this.minWidth;
this.height = this.minHeight;
addEventListener(ChildExistenceChangedEvent.CHILD_ADD, onChildAdd);
addEventListener(ChildExistenceChangedEvent.CHILD_REMOVE, onChildRemove);
addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
}
private function onChildAdd(e:ChildExistenceChangedEvent = null):void {
updateChildrenDimensions(this.getChildren());
}
private function onChildRemove(e:ChildExistenceChangedEvent = null):void {
// copy the children array to "remove" the removing child and update the dimensions correctly
var children:Array = this.getChildren();
var index:int = children.indexOf(e.relatedObject);
if (index != -1)
children.splice(index, 1);
updateChildrenDimensions(children);
}
public function getPrefferedPosition():String{
return MainCanvas.BOTTOM_RIGHT;
}
private function onDragVideoWindow(e:DragWindowEvent):void{
switch (e.mode) {
case DragWindowEvent.DRAG:
if (hitTestPoint(e.mouseGlobal.x, e.mouseGlobal.y, true)) {
setStyle("borderColor","0xFF0000");
e.window.width = e.window.minWidth;
e.window.height = e.window.minHeight;
} else {
setStyle("borderColor",borderColor);
restoreWindowDimensions(e.window);
}
break;
case DragWindowEvent.DRAG_START:
borderColor = getStyle("borderColor");
removeVideoChild(e.window as VideoWindowItf);
saveWindowDimensions(e.window);
break;
case DragWindowEvent.DRAG_END:
setStyle("borderColor",borderColor);
restoreWindowDimensions(e.window);
if (hitTestPoint(e.mouseGlobal.x, e.mouseGlobal.y, true))
addVideoChild(e.window as VideoWindowItf);
break;
}
}
private function saveWindowDimensions(window:MDIWindow):void {
var dimensions:Object = {width:window.width, height:window.height};
childrenDimension[window] = dimensions;
}
private function restoreWindowDimensions(window:MDIWindow):void {
window.width = childrenDimension[window].width;
window.height = childrenDimension[window].height;
}
private function repositionWindow(window:MDIWindow):void {
// \TODO reposition the window correctly between the windows
// setChildIndex(window, ?);
}
private function isVideoWindow(window:Object):Boolean {
return (getQualifiedSuperclassName(window) == "org.bigbluebutton.modules.videoconf.business::VideoWindowItf")
}
private function onCloseWindow(e:CloseWindowEvent):void {
// it should not just undock the window, it should close the window forever
if (isVideoWindow(e.window) && this.contains(e.window as VideoWindowItf))
this.removeChild(e.window as VideoWindowItf);
}
private function onOpenWindow(e:OpenVideoWindowEvent):void {
if (isVideoWindow(e.window) && autoDock)
addVideoChild(e.window as VideoWindowItf);
}
private function addVideoChild(window:VideoWindowItf):void {
if (this.contains(window))
return;
LogUtil.debug("Docking window");
saveWindowDimensions(window);
window.minimizeBtn.visible = false;
window.maximizeRestoreBtn.visible = false;
window.resizable = false;
window.buttonsEnabled = false;
var e:CloseWindowEvent = new CloseWindowEvent();
e.window = window;
dispatchEvent(e);
this.addChild(window);
}
private function removeVideoChild(window:VideoWindowItf):void {
if (!this.contains(window))
return;
window.minimizeBtn.visible = true;
window.maximizeRestoreBtn.visible = true;
window.resizable = true;
window.buttonsEnabled = true;
this.removeChild(window);
var e:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
e.window = window;
dispatchEvent(e);
restoreWindowDimensions(window);
}
override public function close(event:MouseEvent = null):void {
removeAllChildren();
super.close(event);
}
private function updateChildrenDimensions(children:Array):void {
var numChildren:int = children.length;
if (numChildren == 0) return;
var horizontalGap:Number = getStyle("horizontalGap");
var verticalGap:Number = getStyle("verticalGap");
var availableWidth:Number = this.width
- this.borderMetrics.left - this.borderMetrics.right;
var availableHeight:Number = this.height
- this.borderMetrics.top - this.borderMetrics.bottom;
var childWidth:Number = 0;
var childHeight:Number = 0;
var nRows:Number = 0;
var nColumns:Number = 0;
// we would like to maximize the window size
for (var rows:Number = 1; rows <= numChildren; ++rows) {
var columns:Number = Math.ceil(numChildren / rows);
var maxWidth:Number = Math.floor((availableWidth - horizontalGap * (columns - 1)) / columns) - VideoWindowItf.PADDING_HORIZONTAL;
var maxHeight:Number = Math.floor((availableHeight - verticalGap * (rows - 1)) / rows) - VideoWindowItf.PADDING_VERTICAL;
// the possible dimensions shouldn't be less or equal 0 (it could happen with many videos)
if (maxWidth <= 0 || maxHeight <=0)
continue;
var desiredAR:Number = 4/3;
var width:Number = maxWidth;
var height:Number = maxHeight;
if (maxWidth / maxHeight > desiredAR)
width = Math.floor(maxHeight * desiredAR);
else
height = Math.floor(maxWidth / desiredAR);
if (width > childWidth) {
childWidth = width;
childHeight = height;
nRows = rows;
nColumns = columns;
}
}
childWidth += VideoWindowItf.PADDING_HORIZONTAL;
childHeight += VideoWindowItf.PADDING_VERTICAL;
for (var childIndex:int = 0; childIndex < numChildren; ++childIndex) {
var window:VideoWindowItf = children[childIndex];
window.width = childWidth;
window.updateHeight();
if (window.height > childHeight) {
window.height = childHeight;
window.updateWidth();
}
var row:int = childIndex / nColumns;
var column:int = childIndex % nColumns;
var borderTop:int = (availableHeight - nRows * childHeight - (nRows - 1) * verticalGap) / 2;
var borderLeft:int = (availableWidth - nColumns * childWidth - (nColumns - 1) * horizontalGap) / 2;
window.y = row * (childHeight + verticalGap) + borderTop;
window.x = column * (childWidth + horizontalGap) + borderLeft;
}
}
protected function onDoubleClick(event:MouseEvent = null):void {
this.maximizeRestore();
}
override protected function resourcesChanged():void{
super.resourcesChanged();
this.title = ResourceUtil.getInstance().getString('bbb.videodock.title');
}
]]>
</mx:Script>
<mate:Listener type="{DragWindowEvent.DRAG_WINDOW_EVENT}" method="onDragVideoWindow" />
<mate:Listener type="{OpenVideoWindowEvent.OPEN_VIDEO_WINDOW_EVENT}" method="onOpenWindow" />
<mate:Listener type="{CloseWindowEvent.CLOSE_WINDOW_EVENT}" method="onCloseWindow" />
</MDIWindow>

View File

@ -19,8 +19,6 @@
package org.bigbluebutton.util.i18n
{
import com.adobe.utils.StringUtil;
import com.asfusion.mate.events.Dispatcher;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
@ -48,7 +46,7 @@ package org.bigbluebutton.util.i18n
private static var BBB_RESOURCE_BUNDLE:String = 'bbbResources';
public static var DEFAULT_LANGUAGE:String = "en_US";
private static var currentLanguage:String = DEFAULT_LANGUAGE;
private var eventDispatcher:Dispatcher = new Dispatcher();
private var eventDispatcher:IEventDispatcher;
private var localeChain:Array = new Array();
private var resourceManager:IResourceManager;
@ -94,11 +92,23 @@ package org.bigbluebutton.util.i18n
if (resourceManager.localeChain[0] == localeChain[i]) localeAvailable = true;
}
//Locale not found, set default
changeLocale(DEFAULT_LANGUAGE);
/**
* http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/resources/IResourceManager.html#localeChain
* Always load the default language, so if the chosen language
* doesn't provide a resource, the default language resource is used
*/
load(DEFAULT_LANGUAGE);
if (!localeAvailable)
resourceManager.localeChain = [DEFAULT_LANGUAGE];
changeLocale(resourceManager.localeChain[0]);
}
private function load(language:String):IEventDispatcher {
var localeURI:String = 'locale/' + language + '_resources.swf';
return resourceManager.loadResourceModule(localeURI, false);
}
public static function getInstance():ResourceUtil {
if (instance == null) {
LogUtil.debug("Setting up supported locales.");
@ -107,62 +117,41 @@ package org.bigbluebutton.util.i18n
return instance;
}
public function changeLocale(language:String):void{
var localeURI:String = 'locale/' + language + '/bbbResources.properties';
var date:Date = new Date();
var _urlLoader:URLLoader = new URLLoader();
_urlLoader.addEventListener(Event.COMPLETE, handleLocaleLoaded);
_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, handleResourceNotLoaded);
_urlLoader.load(new URLRequest(localeURI + "?a=" + date.time));
currentLanguage = language;
}
private function handleLocaleLoaded(e:Event):void{
var fulltext:String = (e.target.data as String);
fulltext = com.adobe.utils.StringUtil.remove(fulltext, "\r");
var allStrings:Array = fulltext.split("\n");
for (var i:Number=0; i<allStrings.length; i++){
var str:String = allStrings[i] as String;
public function changeLocale(... chain):void{
if(chain != null && chain.length > 0)
{
eventDispatcher = load(chain[0]);
localeChain = [chain[0]];
eventDispatcher.addEventListener(ResourceEvent.COMPLETE, localeChangeComplete);
eventDispatcher.addEventListener(ResourceEvent.ERROR, handleResourceNotLoaded);
if (str.charAt(0) != '#'){
var keyValue:Array = str.split("=");
var key:String = mx.utils.StringUtil.trim(keyValue[0] as String);
var value:String = mx.utils.StringUtil.trim(keyValue[1] as String);
currentLocalization[key] = value;
trace(key + "=" + value);
}
currentLanguage = chain[0];
}
trace(currentLocalization['bbb.logout.usercommand']);
}
private function localeChangeComplete(event:ResourceEvent):void{
if (localeChain[0] != DEFAULT_LANGUAGE)
localeChain.push(DEFAULT_LANGUAGE);
resourceManager.localeChain = localeChain;
update();
}
/**
* Defaults to DEFAULT_LANGUAGE when an error is thrown by the ResourceManager
* @param event
*/
private function handleResourceNotLoaded(event:ResourceEvent):void{
currentLanguage = DEFAULT_LANGUAGE;
update();
}
public function update():void{
eventDispatcher.dispatchEvent(new LocaleChangeEvent(LocaleChangeEvent.LOCALE_CHANGED));
dispatchEvent(new Event(Event.CHANGE));
}
[Bindable("change")]
public function getString(resourceName:String, parameters:Array = null, locale:String = null):String{
if (!parameters) return currentLocalization[resourceName]; //resourceManager.getString(BBB_RESOURCE_BUNDLE, resourceName, parameters, locale);
else return insertParameters(currentLocalization[resourceName], parameters);
/**
* Defaults to DEFAULT_LANGUAGE when an error is thrown by the ResourceManager
* @param event
*/
private function handleResourceNotLoaded(event:ResourceEvent):void{
resourceManager.localeChain = [DEFAULT_LANGUAGE];
update();
}
private function insertParameters(text:String, parameters:Array):String{
return mx.utils.StringUtil.substitute(text, parameters);
public function update():void{
dispatchEvent(new Event(Event.CHANGE));
}
[Bindable("change")]
public function getString(resourceName:String, parameters:Array = null, locale:String = null):String{
return resourceManager.getString(BBB_RESOURCE_BUNDLE, resourceName, parameters, locale);
}
public function getCurrentLanguageCode():String{