Merge branch '090-new-videodock' into HEAD

Conflicts:
	bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala
	bigbluebutton-client/src/org/bigbluebutton/main/views/CameraDisplaySettings.mxml
	bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml
	bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml
	bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as
	bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml
	bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/ToolbarButton.mxml
	record-and-playback/deploy.sh
This commit is contained in:
Felipe Cecagno 2015-01-28 20:33:28 -02:00
commit 9a7bae79a8
67 changed files with 4499 additions and 3900 deletions

View File

@ -42,8 +42,8 @@ public class ParticipantsApplication {
bbbInGW.shareWebcam(meetingId, userId, stream);
}
public void unshareWebcam(String meetingId, String userId) {
bbbInGW.unshareWebcam(meetingId, userId);
public void unshareWebcam(String meetingId, String userId, String stream) {
bbbInGW.unshareWebcam(meetingId, userId, stream);
}
public void setParticipantStatus(String room, String userid, String status, Object value) {

View File

@ -68,10 +68,10 @@ public class ParticipantsService {
application.shareWebcam(scope.getName(), userId, stream);
}
public void unshareWebcam() {
public void unshareWebcam(String stream) {
IScope scope = Red5.getConnectionLocal().getScope();
String userId = getBbbSession().getInternalUserID();
application.unshareWebcam(scope.getName(), userId);
application.unshareWebcam(scope.getName(), userId, stream);
}
public void setParticipantStatus(Map<String, Object> msg) {

View File

@ -33,7 +33,7 @@ public interface IBigBlueButtonInGW {
void userRaiseHand(String meetingId, String userId);
void lowerHand(String meetingId, String userId, String loweredBy);
void shareWebcam(String meetingId, String userId, String stream);
void unshareWebcam(String meetingId, String userId);
void unshareWebcam(String meetingId, String userId, String stream);
void setUserStatus(String meetingID, String userID, String status, Object value);
void getUsers(String meetingID, String requesterID);
void userLeft(String meetingID, String userID);

View File

@ -153,8 +153,8 @@ class BigBlueButtonInGW(bbbGW: BigBlueButtonGateway, presUtil: PreuploadedPresen
bbbGW.accept(new UserShareWebcam(meetingId, userId, stream))
}
def unshareWebcam(meetingId: String, userId: String) {
bbbGW.accept(new UserUnshareWebcam(meetingId, userId))
def unshareWebcam(meetingId: String, userId: String, stream:String) {
bbbGW.accept(new UserUnshareWebcam(meetingId, userId, stream))
}
def setUserStatus(meetingID: String, userID: String, status: String, value: Object):Unit = {

View File

@ -206,7 +206,7 @@ class CollectorActor(dispatcher: IDispatcher) extends Actor {
wuser.put(Constants.PRESENTER, user.presenter:java.lang.Boolean)
wuser.put(Constants.HAS_STREAM, user.hasStream:java.lang.Boolean)
wuser.put(Constants.LOCKED, user.locked:java.lang.Boolean)
wuser.put(Constants.WEBCAM_STREAM, user.webcamStream)
wuser.put("webcamStream", user.webcamStreams mkString("|"))
wuser.put(Constants.PHONE_USER, user.phoneUser:java.lang.Boolean)
wuser.put(Constants.VOICE_USER, vuser)

View File

@ -127,7 +127,8 @@ case class UserShareWebcam(
case class UserUnshareWebcam(
meetingID: String,
userId: String
userId: String,
stream: String
) extends InMessage
case class ChangeUserStatus(

View File

@ -67,7 +67,7 @@ case class UserVO(
presenter: Boolean,
hasStream: Boolean,
locked: Boolean,
webcamStream: String,
webcamStreams: Set[String],
phoneUser: Boolean,
voiceUser: VoiceUser,
listenOnly: Boolean,

View File

@ -1,430 +1,432 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.core.api._
import scala.collection.mutable.HashMap
import org.bigbluebutton.core.User
import java.util.ArrayList
import org.bigbluebutton.core.MeetingActor
import scala.collection.mutable.ArrayBuffer
trait UsersApp {
this : MeetingActor =>
val outGW: MessageOutGateway
val users = new UsersModel
private var regUsers = new collection.immutable.HashMap[String, RegisteredUser]
private var locked = false
private var meetingMuted = false
private var currentPresenter = new Presenter("system", "system", "system")
def hasUser(userID: String):Boolean = {
users.hasUser(userID)
}
def getUser(userID:String):Option[UserVO] = {
users.getUser(userID)
}
def getCurrentPresenter:Presenter = {
currentPresenter
}
def handleUserConnectedToGlobalAudio(msg: UserConnectedToGlobalAudio) {
val user = users.getUserWithExternalId(msg.userid)
user foreach {u =>
val vu = u.voiceUser.copy(talking=false)
val uvo = u.copy(listenOnly=true, voiceUser=vu)
users.addUser(uvo)
logger.info("UserConnectedToGlobalAudio: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserListeningOnly(meetingID, recorded, uvo.userID, uvo.listenOnly))
}
}
def handleUserDisconnectedFromGlobalAudio(msg: UserDisconnectedFromGlobalAudio) {
val user = users.getUserWithExternalId(msg.userid)
user foreach {u =>
val uvo = u.copy(listenOnly=false)
users.addUser(uvo)
logger.info("UserDisconnectedToGlobalAudio: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserListeningOnly(meetingID, recorded, uvo.userID, uvo.listenOnly))
}
}
def handleMuteAllExceptPresenterRequest(msg: MuteAllExceptPresenterRequest) {
meetingMuted = msg.mute
outGW.send(new MeetingMuted(meetingID, recorded, meetingMuted))
usersWhoAreNotPresenter foreach {u =>
outGW.send(new MuteVoiceUser(meetingID, recorded, msg.requesterID, u.userID, msg.mute))
}
}
def handleMuteMeetingRequest(msg: MuteMeetingRequest) {
meetingMuted = msg.mute
outGW.send(new MeetingMuted(meetingID, recorded, meetingMuted))
users.getUsers foreach {u =>
outGW.send(new MuteVoiceUser(meetingID, recorded, msg.requesterID, u.userID, msg.mute))
}
}
def handleValidateAuthToken(msg: ValidateAuthToken) {
// println("*************** Got ValidateAuthToken message ********************" )
regUsers.get (msg.userId) match {
case Some(u) =>
{
val replyTo = meetingID + '/' + msg.userId
//send the reply
outGW.send(new ValidateAuthTokenReply(meetingID, msg.userId, msg.token, true, msg.correlationId))
//send the list of users in the meeting
outGW.send(new GetUsersReply(meetingID, msg.userId, users.getUsers))
//send chat history
this ! (new GetChatHistoryRequest(meetingID, msg.userId, replyTo))
//join the user
handleUserJoin(new UserJoining(meetingID, msg.userId))
//send the presentation
logger.info("ValidateToken success: mid=[" + meetingID + "] uid=[" + msg.userId + "]")
this ! (new GetPresentationInfo(meetingID, msg.userId, replyTo))
}
case None => {
logger.info("ValidateToken failed: mid=[" + meetingID + "] uid=[" + msg.userId + "]")
outGW.send(new ValidateAuthTokenReply(meetingID, msg.userId, msg.token, false, msg.correlationId))
}
}
}
def handleRegisterUser(msg: RegisterUser) {
if (hasMeetingEnded) {
// Check first if the meeting has ended and the user refreshed the client to re-connect.
logger.info("Register user failed: reason=[meeting has ended] mid=[" + meetingID + "] uid=[" + msg.userID + "]")
sendMeetingHasEnded(msg.userID)
} else {
val regUser = new RegisteredUser(msg.userID, msg.extUserID, msg.name, msg.role, msg.authToken)
regUsers += msg.userID -> regUser
logger.info("Register user success: mid=[" + meetingID + "] uid=[" + msg.userID + "]")
outGW.send(new UserRegistered(meetingID, recorded, regUser))
}
}
def handleIsMeetingMutedRequest(msg: IsMeetingMutedRequest) {
outGW.send(new IsMeetingMutedReply(meetingID, recorded, msg.requesterID, meetingMuted))
}
def handleMuteUserRequest(msg: MuteUserRequest) {
// println("Received mute user request uid=[" + msg.userID + "] mute=[" + msg.mute + "]")
users.getUser(msg.userID) match {
case Some(u) => {
// println("Sending mute user request uid=[" + msg.userID + "] mute=[" + msg.mute + "]")
logger.info("Muting user: mid=[" + meetingID + "] uid=[" + u.userID + "]")
outGW.send(new MuteVoiceUser(meetingID, recorded, msg.requesterID, u.userID, msg.mute))
}
case None => {
logger.info("Could not find user to mute: mid=[" + meetingID + "] uid=[" + msg.userID + "]")
// println("Could not find user to mute. uid=[" + msg.userID + "] mute=[" + msg.mute + "]")
}
}
}
def handleEjectUserRequest(msg: EjectUserFromVoiceRequest) {
// println("Received eject user request uid=[" + msg.userID + "]")
users.getUser(msg.userId) match {
case Some(u) => {
if (u.voiceUser.joined) {
logger.info("Ejecting user from voice: mid=[" + meetingID + "] uid=[" + u.userID + "]")
outGW.send(new EjectVoiceUser(meetingID, recorded, msg.ejectedBy, u.userID))
}
}
case None => // do nothing
}
}
def handleLockUser(msg: LockUser) {
}
def handleLockAllUsers(msg: LockAllUsers) {
}
def handleGetLockSettings(msg: GetLockSettings) {
}
def handleIsMeetingLocked(msg: IsMeetingLocked) {
}
def handleSetLockSettings(msg: SetLockSettings) {
// println("*************** Received new lock settings ********************")
if (!permissionsEqual(msg.settings)) {
newPermissions(msg.settings)
val au = affectedUsers(msg.settings)
outGW.send(new NewPermissionsSetting(meetingID, msg.setByUser, permissions, au))
handleLockLayout(msg.settings.lockedLayout, msg.setByUser)
}
}
def handleInitLockSettings(msg: InitLockSettings) {
if (! permissionsInited) {
permissionsInited = true
if (permissions != msg.settings || locked != msg.locked) {
permissions = msg.settings
locked = msg.locked
val au = affectedUsers(msg.settings)
outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.locked, msg.settings, au))
}
}
}
def usersWhoAreNotPresenter():Array[UserVO] = {
val au = ArrayBuffer[UserVO]()
users.getUsers foreach {u =>
if (! u.presenter) {
au += u
}
}
au.toArray
}
def affectedUsers(settings: Permissions):Array[UserVO] = {
val au = ArrayBuffer[UserVO]()
users.getUsers foreach {u =>
val nu = u.copy(permissions=permissions)
users.addUser(nu)
if (! u.presenter && u.role != Role.MODERATOR) {
au += nu
}
}
au.toArray
}
def handleUserRaiseHand(msg: UserRaiseHand) {
users.getUser(msg.userId) foreach {user =>
val uvo = user.copy(raiseHand=true)
users.addUser(uvo)
outGW.send(new UserRaisedHand(meetingID, recorded, uvo.userID))
}
}
def handleUserLowerHand(msg: UserLowerHand) {
users.getUser(msg.userId) foreach {user =>
val uvo = user.copy(raiseHand=false)
users.addUser(uvo)
outGW.send(new UserLoweredHand(meetingID, recorded, uvo.userID, msg.loweredBy))
}
}
def handleEjectUserFromMeeting(msg: EjectUserFromMeeting) {
users.getUser(msg.userId) foreach {user =>
if (user.voiceUser.joined) {
outGW.send(new EjectVoiceUser(meetingID, recorded, msg.ejectedBy, msg.userId))
}
users.removeUser(msg.userId)
logger.info("Ejecting user from meeting: mid=[" + meetingID + "]uid=[" + msg.userId + "]")
outGW.send(new UserEjectedFromMeeting(meetingID, recorded, msg.userId, msg.ejectedBy))
outGW.send(new DisconnectUser(meetingID, msg.userId))
outGW.send(new UserLeft(msg.meetingID, recorded, user))
}
}
def handleUserShareWebcam(msg: UserShareWebcam) {
users.getUser(msg.userId) foreach {user =>
val uvo = user.copy(hasStream=true, webcamStream=msg.stream)
users.addUser(uvo)
logger.info("User shared webcam: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserSharedWebcam(meetingID, recorded, uvo.userID, msg.stream))
}
}
def handleUserunshareWebcam(msg: UserUnshareWebcam) {
users.getUser(msg.userId) foreach {user =>
val stream = user.webcamStream
val uvo = user.copy(hasStream=false, webcamStream="")
users.addUser(uvo)
logger.info("User unshared webcam: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserUnsharedWebcam(meetingID, recorded, uvo.userID, stream))
}
}
def handleChangeUserStatus(msg: ChangeUserStatus):Unit = {
if (users.hasUser(msg.userID)) {
outGW.send(new UserStatusChange(meetingID, recorded, msg.userID, msg.status, msg.value))
}
}
def handleGetUsers(msg: GetUsers):Unit = {
outGW.send(new GetUsersReply(msg.meetingID, msg.requesterID, users.getUsers))
}
def handleUserJoin(msg: UserJoining):Unit = {
val regUser = regUsers.get(msg.userID)
regUser foreach { ru =>
val vu = new VoiceUser(msg.userID, msg.userID, ru.name, ru.name,
false, false, false, false)
val uvo = new UserVO(msg.userID, ru.externId, ru.name,
ru.role, raiseHand=false, presenter=false,
hasStream=false, locked=false, webcamStream="",
phoneUser=false, vu, listenOnly=false, permissions)
users.addUser(uvo)
logger.info("User joined meeting: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserJoined(meetingID, recorded, uvo))
outGW.send(new MeetingState(meetingID, recorded, uvo.userID, permissions, meetingMuted))
// Become presenter if the only moderator
if (users.numModerators == 1) {
if (ru.role == Role.MODERATOR) {
assignNewPresenter(msg.userID, ru.name, msg.userID)
}
}
webUserJoined
startRecordingIfAutoStart()
}
}
def handleUserLeft(msg: UserLeaving):Unit = {
if (users.hasUser(msg.userID)) {
val user = users.removeUser(msg.userID)
user foreach { u =>
logger.info("User left meeting: mid=[" + meetingID + "] uid=[" + u.userID + "]")
outGW.send(new UserLeft(msg.meetingID, recorded, u))
}
startCheckingIfWeNeedToEndVoiceConf()
stopAutoStartedRecording()
}
}
def handleUserJoinedVoiceFromPhone(msg: VoiceUserJoined) = {
val user = users.getUserWithVoiceUserId(msg.voiceUser.userId) match {
case Some(user) => {
logger.info("Voice user=[" + msg.voiceUser.userId + "] is already in conf=[" + voiceBridge + "]. Must be duplicate message.")
}
case None => {
// No current web user. This means that the user called in through
// the phone. We need to generate a new user as we are not able
// to match with a web user.
val webUserId = users.generateWebUserId
val vu = new VoiceUser(msg.voiceUser.userId, webUserId,
msg.voiceUser.callerName, msg.voiceUser.callerNum,
true, false, false, false)
val uvo = new UserVO(webUserId, webUserId, msg.voiceUser.callerName,
Role.VIEWER, raiseHand=false, presenter=false,
hasStream=false, locked=false, webcamStream="",
phoneUser=true, vu, listenOnly=false, permissions)
users.addUser(uvo)
logger.info("New user joined voice for user [" + uvo.name + "] userid=[" + msg.voiceUser.webUserId + "]")
outGW.send(new UserJoined(meetingID, recorded, uvo))
outGW.send(new UserJoinedVoice(meetingID, recorded, voiceBridge, uvo))
if (meetingMuted)
outGW.send(new MuteVoiceUser(meetingID, recorded, uvo.userID, uvo.userID, meetingMuted))
}
}
}
def handleVoiceUserJoined(msg: VoiceUserJoined) = {
val user = users.getUser(msg.voiceUser.webUserId) match {
case Some(user) => {
val nu = user.copy(voiceUser=msg.voiceUser)
users.addUser(nu)
logger.info("Received user joined voice for user [" + nu.name + "] userid=[" + msg.voiceUser.webUserId + "]" )
outGW.send(new UserJoinedVoice(meetingID, recorded, voiceBridge, nu))
if (meetingMuted)
outGW.send(new MuteVoiceUser(meetingID, recorded, nu.userID, nu.userID, meetingMuted))
}
case None => {
handleUserJoinedVoiceFromPhone(msg)
}
}
}
def handleVoiceUserLeft(msg: VoiceUserLeft) {
users.getUser(msg.userId) foreach {user =>
val vu = new VoiceUser(user.userID, user.userID, user.name, user.name,
false, false, false, false)
val nu = user.copy(voiceUser=vu)
users.addUser(nu)
// println("Received voice user left =[" + user.name + "] wid=[" + msg.userId + "]" )
logger.info("Received user left voice for user [" + nu.name + "] userid=[" + msg.userId + "]" )
outGW.send(new UserLeftVoice(meetingID, recorded, voiceBridge, nu))
if (user.phoneUser) {
if (users.hasUser(user.userID)) {
val userLeaving = users.removeUser(user.userID)
userLeaving foreach (u => outGW.send(new UserLeft(msg.meetingID, recorded, u)))
}
}
}
}
def handleVoiceUserMuted(msg: VoiceUserMuted) {
users.getUser(msg.userId) foreach {user =>
val talking = if (msg.muted) false else user.voiceUser.talking
val nv = user.voiceUser.copy(muted=msg.muted, talking=talking)
val nu = user.copy(voiceUser=nv)
users.addUser(nu)
// println("Received voice muted=[" + msg.muted + "] wid=[" + msg.userId + "]" )
outGW.send(new UserVoiceMuted(meetingID, recorded, voiceBridge, nu))
}
}
def handleVoiceUserTalking(msg: VoiceUserTalking) {
users.getUser(msg.userId) foreach {user =>
val nv = user.voiceUser.copy(talking=msg.talking)
val nu = user.copy(voiceUser=nv)
users.addUser(nu)
// println("Received voice talking=[" + msg.talking + "] wid=[" + msg.userId + "]" )
outGW.send(new UserVoiceTalking(meetingID, recorded, voiceBridge, nu))
}
}
def handleAssignPresenter(msg: AssignPresenter):Unit = {
assignNewPresenter(msg.newPresenterID, msg.newPresenterName, msg.assignedBy)
}
def assignNewPresenter(newPresenterID:String, newPresenterName: String, assignedBy: String) {
if (users.hasUser(newPresenterID)) {
users.getCurrentPresenter match {
case Some(curPres) => {
users.unbecomePresenter(curPres.userID)
outGW.send(new UserStatusChange(meetingID, recorded, curPres.userID, "presenter", false:java.lang.Boolean))
}
case None => // do nothing
}
users.getUser(newPresenterID) match {
case Some(newPres) => {
users.becomePresenter(newPres.userID)
currentPresenter = new Presenter(newPresenterID, newPresenterName, assignedBy)
outGW.send(new PresenterAssigned(meetingID, recorded, new Presenter(newPresenterID, newPresenterName, assignedBy)))
outGW.send(new UserStatusChange(meetingID, recorded, newPresenterID, "presenter", true:java.lang.Boolean))
}
case None => // do nothing
}
}
}
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.core.api._
import scala.collection.mutable.HashMap
import org.bigbluebutton.core.User
import java.util.ArrayList
import org.bigbluebutton.core.MeetingActor
import scala.collection.mutable.ArrayBuffer
import scala.collection.immutable.ListSet
trait UsersApp {
this : MeetingActor =>
val outGW: MessageOutGateway
val users = new UsersModel
private var regUsers = new collection.immutable.HashMap[String, RegisteredUser]
private var locked = false
private var meetingMuted = false
private var currentPresenter = new Presenter("system", "system", "system")
def hasUser(userID: String):Boolean = {
users.hasUser(userID)
}
def getUser(userID:String):Option[UserVO] = {
users.getUser(userID)
}
def getCurrentPresenter:Presenter = {
currentPresenter
}
def handleUserConnectedToGlobalAudio(msg: UserConnectedToGlobalAudio) {
val user = users.getUserWithExternalId(msg.userid)
user foreach {u =>
val vu = u.voiceUser.copy(talking=false)
val uvo = u.copy(listenOnly=true, voiceUser=vu)
users.addUser(uvo)
logger.info("UserConnectedToGlobalAudio: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserListeningOnly(meetingID, recorded, uvo.userID, uvo.listenOnly))
}
}
def handleUserDisconnectedFromGlobalAudio(msg: UserDisconnectedFromGlobalAudio) {
val user = users.getUserWithExternalId(msg.userid)
user foreach {u =>
val uvo = u.copy(listenOnly=false)
users.addUser(uvo)
logger.info("UserDisconnectedToGlobalAudio: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserListeningOnly(meetingID, recorded, uvo.userID, uvo.listenOnly))
}
}
def handleMuteAllExceptPresenterRequest(msg: MuteAllExceptPresenterRequest) {
meetingMuted = msg.mute
outGW.send(new MeetingMuted(meetingID, recorded, meetingMuted))
usersWhoAreNotPresenter foreach {u =>
outGW.send(new MuteVoiceUser(meetingID, recorded, msg.requesterID, u.userID, msg.mute))
}
}
def handleMuteMeetingRequest(msg: MuteMeetingRequest) {
meetingMuted = msg.mute
outGW.send(new MeetingMuted(meetingID, recorded, meetingMuted))
users.getUsers foreach {u =>
outGW.send(new MuteVoiceUser(meetingID, recorded, msg.requesterID, u.userID, msg.mute))
}
}
def handleValidateAuthToken(msg: ValidateAuthToken) {
// println("*************** Got ValidateAuthToken message ********************" )
regUsers.get (msg.userId) match {
case Some(u) =>
{
val replyTo = meetingID + '/' + msg.userId
//send the reply
outGW.send(new ValidateAuthTokenReply(meetingID, msg.userId, msg.token, true, msg.correlationId))
//send the list of users in the meeting
outGW.send(new GetUsersReply(meetingID, msg.userId, users.getUsers))
//send chat history
this ! (new GetChatHistoryRequest(meetingID, msg.userId, replyTo))
//join the user
handleUserJoin(new UserJoining(meetingID, msg.userId))
//send the presentation
logger.info("ValidateToken success: mid=[" + meetingID + "] uid=[" + msg.userId + "]")
this ! (new GetPresentationInfo(meetingID, msg.userId, replyTo))
}
case None => {
logger.info("ValidateToken failed: mid=[" + meetingID + "] uid=[" + msg.userId + "]")
outGW.send(new ValidateAuthTokenReply(meetingID, msg.userId, msg.token, false, msg.correlationId))
}
}
}
def handleRegisterUser(msg: RegisterUser) {
if (hasMeetingEnded) {
// Check first if the meeting has ended and the user refreshed the client to re-connect.
logger.info("Register user failed: reason=[meeting has ended] mid=[" + meetingID + "] uid=[" + msg.userID + "]")
sendMeetingHasEnded(msg.userID)
} else {
val regUser = new RegisteredUser(msg.userID, msg.extUserID, msg.name, msg.role, msg.authToken)
regUsers += msg.userID -> regUser
logger.info("Register user success: mid=[" + meetingID + "] uid=[" + msg.userID + "]")
outGW.send(new UserRegistered(meetingID, recorded, regUser))
}
}
def handleIsMeetingMutedRequest(msg: IsMeetingMutedRequest) {
outGW.send(new IsMeetingMutedReply(meetingID, recorded, msg.requesterID, meetingMuted))
}
def handleMuteUserRequest(msg: MuteUserRequest) {
// println("Received mute user request uid=[" + msg.userID + "] mute=[" + msg.mute + "]")
users.getUser(msg.userID) match {
case Some(u) => {
// println("Sending mute user request uid=[" + msg.userID + "] mute=[" + msg.mute + "]")
logger.info("Muting user: mid=[" + meetingID + "] uid=[" + u.userID + "]")
outGW.send(new MuteVoiceUser(meetingID, recorded, msg.requesterID, u.userID, msg.mute))
}
case None => {
logger.info("Could not find user to mute: mid=[" + meetingID + "] uid=[" + msg.userID + "]")
// println("Could not find user to mute. uid=[" + msg.userID + "] mute=[" + msg.mute + "]")
}
}
}
def handleEjectUserRequest(msg: EjectUserFromVoiceRequest) {
// println("Received eject user request uid=[" + msg.userID + "]")
users.getUser(msg.userId) match {
case Some(u) => {
if (u.voiceUser.joined) {
logger.info("Ejecting user from voice: mid=[" + meetingID + "] uid=[" + u.userID + "]")
outGW.send(new EjectVoiceUser(meetingID, recorded, msg.ejectedBy, u.userID))
}
}
case None => // do nothing
}
}
def handleLockUser(msg: LockUser) {
}
def handleLockAllUsers(msg: LockAllUsers) {
}
def handleGetLockSettings(msg: GetLockSettings) {
}
def handleIsMeetingLocked(msg: IsMeetingLocked) {
}
def handleSetLockSettings(msg: SetLockSettings) {
// println("*************** Received new lock settings ********************")
if (!permissionsEqual(msg.settings)) {
newPermissions(msg.settings)
val au = affectedUsers(msg.settings)
outGW.send(new NewPermissionsSetting(meetingID, msg.setByUser, permissions, au))
handleLockLayout(msg.settings.lockedLayout, msg.setByUser)
}
}
def handleInitLockSettings(msg: InitLockSettings) {
if (! permissionsInited) {
permissionsInited = true
if (permissions != msg.settings || locked != msg.locked) {
permissions = msg.settings
locked = msg.locked
val au = affectedUsers(msg.settings)
outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.locked, msg.settings, au))
}
}
}
def usersWhoAreNotPresenter():Array[UserVO] = {
val au = ArrayBuffer[UserVO]()
users.getUsers foreach {u =>
if (! u.presenter) {
au += u
}
}
au.toArray
}
def affectedUsers(settings: Permissions):Array[UserVO] = {
val au = ArrayBuffer[UserVO]()
users.getUsers foreach {u =>
val nu = u.copy(permissions=permissions)
users.addUser(nu)
if (! u.presenter && u.role != Role.MODERATOR) {
au += nu
}
}
au.toArray
}
def handleUserRaiseHand(msg: UserRaiseHand) {
users.getUser(msg.userId) foreach {user =>
val uvo = user.copy(raiseHand=true)
users.addUser(uvo)
outGW.send(new UserRaisedHand(meetingID, recorded, uvo.userID))
}
}
def handleUserLowerHand(msg: UserLowerHand) {
users.getUser(msg.userId) foreach {user =>
val uvo = user.copy(raiseHand=false)
users.addUser(uvo)
outGW.send(new UserLoweredHand(meetingID, recorded, uvo.userID, msg.loweredBy))
}
}
def handleEjectUserFromMeeting(msg: EjectUserFromMeeting) {
users.getUser(msg.userId) foreach {user =>
if (user.voiceUser.joined) {
outGW.send(new EjectVoiceUser(meetingID, recorded, msg.ejectedBy, msg.userId))
}
users.removeUser(msg.userId)
logger.info("Ejecting user from meeting: mid=[" + meetingID + "]uid=[" + msg.userId + "]")
outGW.send(new UserEjectedFromMeeting(meetingID, recorded, msg.userId, msg.ejectedBy))
outGW.send(new DisconnectUser(meetingID, msg.userId))
outGW.send(new UserLeft(msg.meetingID, recorded, user))
}
}
def handleUserShareWebcam(msg: UserShareWebcam) {
users.getUser(msg.userId) foreach {user =>
val streams = user.webcamStreams + msg.stream
val uvo = user.copy(hasStream=true, webcamStreams=streams)
users.addUser(uvo)
logger.info("User shared webcam: mid=[" + meetingID + "] uid=[" + uvo.userID + "] sharedStream=[" + msg.stream + "] streams=[" + streams + "]")
outGW.send(new UserSharedWebcam(meetingID, recorded, uvo.userID, msg.stream))
}
}
def handleUserunshareWebcam(msg: UserUnshareWebcam) {
users.getUser(msg.userId) foreach {user =>
val streams = user.webcamStreams - msg.stream
val uvo = user.copy(hasStream=(!streams.isEmpty), webcamStreams=streams)
users.addUser(uvo)
logger.info("User unshared webcam: mid=[" + meetingID + "] uid=[" + uvo.userID + "] unsharedStream=[" + msg.stream + "] streams=[" + streams + "]")
outGW.send(new UserUnsharedWebcam(meetingID, recorded, uvo.userID, msg.stream))
}
}
def handleChangeUserStatus(msg: ChangeUserStatus):Unit = {
if (users.hasUser(msg.userID)) {
outGW.send(new UserStatusChange(meetingID, recorded, msg.userID, msg.status, msg.value))
}
}
def handleGetUsers(msg: GetUsers):Unit = {
outGW.send(new GetUsersReply(msg.meetingID, msg.requesterID, users.getUsers))
}
def handleUserJoin(msg: UserJoining):Unit = {
val regUser = regUsers.get(msg.userID)
regUser foreach { ru =>
val vu = new VoiceUser(msg.userID, msg.userID, ru.name, ru.name,
false, false, false, false)
val uvo = new UserVO(msg.userID, ru.externId, ru.name,
ru.role, raiseHand=false, presenter=false,
hasStream=false, locked=false, webcamStreams=new ListSet[String](),
phoneUser=false, vu, listenOnly=false, permissions)
users.addUser(uvo)
logger.info("User joined meeting: mid=[" + meetingID + "] uid=[" + uvo.userID + "]")
outGW.send(new UserJoined(meetingID, recorded, uvo))
outGW.send(new MeetingState(meetingID, recorded, uvo.userID, permissions, meetingMuted))
// Become presenter if the only moderator
if (users.numModerators == 1) {
if (ru.role == Role.MODERATOR) {
assignNewPresenter(msg.userID, ru.name, msg.userID)
}
}
webUserJoined
startRecordingIfAutoStart()
}
}
def handleUserLeft(msg: UserLeaving):Unit = {
if (users.hasUser(msg.userID)) {
val user = users.removeUser(msg.userID)
user foreach { u =>
logger.info("User left meeting: mid=[" + meetingID + "] uid=[" + u.userID + "]")
outGW.send(new UserLeft(msg.meetingID, recorded, u))
}
startCheckingIfWeNeedToEndVoiceConf()
stopAutoStartedRecording()
}
}
def handleUserJoinedVoiceFromPhone(msg: VoiceUserJoined) = {
val user = users.getUserWithVoiceUserId(msg.voiceUser.userId) match {
case Some(user) => {
logger.info("Voice user=[" + msg.voiceUser.userId + "] is already in conf=[" + voiceBridge + "]. Must be duplicate message.")
}
case None => {
// No current web user. This means that the user called in through
// the phone. We need to generate a new user as we are not able
// to match with a web user.
val webUserId = users.generateWebUserId
val vu = new VoiceUser(msg.voiceUser.userId, webUserId,
msg.voiceUser.callerName, msg.voiceUser.callerNum,
true, false, false, false)
val uvo = new UserVO(webUserId, webUserId, msg.voiceUser.callerName,
Role.VIEWER, raiseHand=false, presenter=false,
hasStream=false, locked=false, webcamStreams=new ListSet[String](),
phoneUser=true, vu, listenOnly=false, permissions)
users.addUser(uvo)
logger.info("New user joined voice for user [" + uvo.name + "] userid=[" + msg.voiceUser.webUserId + "]")
outGW.send(new UserJoined(meetingID, recorded, uvo))
outGW.send(new UserJoinedVoice(meetingID, recorded, voiceBridge, uvo))
if (meetingMuted)
outGW.send(new MuteVoiceUser(meetingID, recorded, uvo.userID, uvo.userID, meetingMuted))
}
}
}
def handleVoiceUserJoined(msg: VoiceUserJoined) = {
val user = users.getUser(msg.voiceUser.webUserId) match {
case Some(user) => {
val nu = user.copy(voiceUser=msg.voiceUser)
users.addUser(nu)
logger.info("Received user joined voice for user [" + nu.name + "] userid=[" + msg.voiceUser.webUserId + "]" )
outGW.send(new UserJoinedVoice(meetingID, recorded, voiceBridge, nu))
if (meetingMuted)
outGW.send(new MuteVoiceUser(meetingID, recorded, nu.userID, nu.userID, meetingMuted))
}
case None => {
handleUserJoinedVoiceFromPhone(msg)
}
}
}
def handleVoiceUserLeft(msg: VoiceUserLeft) {
users.getUser(msg.userId) foreach {user =>
val vu = new VoiceUser(user.userID, user.userID, user.name, user.name,
false, false, false, false)
val nu = user.copy(voiceUser=vu)
users.addUser(nu)
// println("Received voice user left =[" + user.name + "] wid=[" + msg.userId + "]" )
logger.info("Received user left voice for user [" + nu.name + "] userid=[" + msg.userId + "]" )
outGW.send(new UserLeftVoice(meetingID, recorded, voiceBridge, nu))
if (user.phoneUser) {
if (users.hasUser(user.userID)) {
val userLeaving = users.removeUser(user.userID)
userLeaving foreach (u => outGW.send(new UserLeft(msg.meetingID, recorded, u)))
}
}
}
}
def handleVoiceUserMuted(msg: VoiceUserMuted) {
users.getUser(msg.userId) foreach {user =>
val talking = if (msg.muted) false else user.voiceUser.talking
val nv = user.voiceUser.copy(muted=msg.muted, talking=talking)
val nu = user.copy(voiceUser=nv)
users.addUser(nu)
// println("Received voice muted=[" + msg.muted + "] wid=[" + msg.userId + "]" )
outGW.send(new UserVoiceMuted(meetingID, recorded, voiceBridge, nu))
}
}
def handleVoiceUserTalking(msg: VoiceUserTalking) {
users.getUser(msg.userId) foreach {user =>
val nv = user.voiceUser.copy(talking=msg.talking)
val nu = user.copy(voiceUser=nv)
users.addUser(nu)
// println("Received voice talking=[" + msg.talking + "] wid=[" + msg.userId + "]" )
outGW.send(new UserVoiceTalking(meetingID, recorded, voiceBridge, nu))
}
}
def handleAssignPresenter(msg: AssignPresenter):Unit = {
assignNewPresenter(msg.newPresenterID, msg.newPresenterName, msg.assignedBy)
}
def assignNewPresenter(newPresenterID:String, newPresenterName: String, assignedBy: String) {
if (users.hasUser(newPresenterID)) {
users.getCurrentPresenter match {
case Some(curPres) => {
users.unbecomePresenter(curPres.userID)
outGW.send(new UserStatusChange(meetingID, recorded, curPres.userID, "presenter", false:java.lang.Boolean))
}
case None => // do nothing
}
users.getUser(newPresenterID) match {
case Some(newPres) => {
users.becomePresenter(newPres.userID)
currentPresenter = new Presenter(newPresenterID, newPresenterName, assignedBy)
outGW.send(new PresenterAssigned(meetingID, recorded, new Presenter(newPresenterID, newPresenterName, assignedBy)))
outGW.send(new UserStatusChange(meetingID, recorded, newPresenterID, "presenter", true:java.lang.Boolean))
}
case None => // do nothing
}
}
}
}

View File

@ -3,6 +3,7 @@ package org.bigbluebutton.core.apps.users.red5
import org.bigbluebutton.conference.meeting.messaging.red5.ConnectionInvokerService
import org.bigbluebutton.conference.meeting.messaging.red5.SharedObjectClientMessage
import java.util.ArrayList
import java.util.List
import java.util.Map
import java.util.HashMap
import org.bigbluebutton.core.api._
@ -78,7 +79,7 @@ class UsersClientMessageSender(service: ConnectionInvokerService) extends OutMes
wuser.put("presenter", user.presenter:java.lang.Boolean)
wuser.put("hasStream", user.hasStream:java.lang.Boolean)
wuser.put("locked", user.locked:java.lang.Boolean)
wuser.put("webcamStream", user.webcamStream)
wuser.put("webcamStream", user.webcamStreams mkString("|"))
wuser.put("phoneUser", user.phoneUser:java.lang.Boolean)
wuser.put("voiceUser", vuser)
wuser.put("listenOnly", user.listenOnly:java.lang.Boolean)
@ -421,6 +422,7 @@ class UsersClientMessageSender(service: ConnectionInvokerService) extends OutMes
def handleUserUnshareWebcam(msg: UserUnsharedWebcam) {
var args = new HashMap[String, Object]()
args.put("userId", msg.userID)
args.put("webcamStream", msg.stream)
val message = new java.util.HashMap[String, Object]()
val gson = new Gson();

View File

@ -20,7 +20,7 @@ object UsersMessageToJsonConverter {
wuser += "presenter" -> user.presenter
wuser += "has_stream" -> user.hasStream
wuser += "locked" -> user.locked
wuser += "webcam_stream" -> user.webcamStream
wuser += "webcam_stream" -> user.webcamStreams
wuser += "phone_user" -> user.phoneUser
wuser += "listenOnly" -> user.listenOnly

View File

@ -64,7 +64,42 @@ ToolTip {
color: #e1e2e5;
}
Button, .logoutButtonStyle, .chatSendButtonStyle, .helpLinkButtonStyle, .cameraDisplaySettingsWindowChangeResolutionCombo, .languageSelectorStyle, .testJavaLinkButtonStyle, .recordButtonStyleNormal, .recordButtonStyleStart, .recordButtonStyleStop, .micSettingsWindowHelpButtonStyle {
.videoMessageWarningLabelStyle {
fontWeight: bold;
fontSize: 18;
fontFamily: Arial;
color: #ffff26;
}
.videoMessageErrorLabelStyle {
fontWeight: bold;
fontSize: 18;
fontFamily: Arial;
color: #ff2727;
}
.videoMessageBackgroundStyle {
backgroundAlpha: 0.6;
backgroundColor: #b9babc;
}
.videoToolbarBackgroundTalkingStyle {
backgroundColor: #20c600;
backgroundAlpha: 0.6;
}
.videoToolbarBackgroundNotTalkingStyle {
backgroundColor: #42444c;
backgroundAlpha: 0.6;
}
.videoToolbarLabelStyle {
textIndent: 0;
color: #ffffff;
fontFamily: Arial;
}
Button, .logoutButtonStyle, .chatSendButtonStyle, .helpLinkButtonStyle, .cameraDisplaySettingsWindowProfileComboStyle, .cameraDisplaySettingsWindowCameraSelector, .languageSelectorStyle, .testJavaLinkButtonStyle, .recordButtonStyleNormal, .recordButtonStyleStart, .recordButtonStyleStop, .micSettingsWindowHelpButtonStyle {
textIndent: 0;
paddingLeft: 10;
paddingRight: 10;
@ -767,6 +802,27 @@ MDIWindow { /*None of the following properties are overridden by the MDIWindow c
disabledSkin: Embed('assets/images/3_minimizeButton.png');
}
/*
https://www.iconfinder.com/icons/172512/mute_icon#size=128
*/
.muteOverlayBtn
{
upSkin: Embed('assets/images/audio_20_white.png');
overSkin: Embed('assets/images/audio_20_white.png');
downSkin: Embed('assets/images/audio_20_white.png');
disabledSkin: Embed('assets/images/audio_20_white.png');
}
/*
https://www.iconfinder.com/icons/172499/low_volume_icon#size=128
*/
.unmuteOverlayBtn
{
upSkin: Embed('assets/images/audio_muted_20_white.png');
overSkin: Embed('assets/images/audio_muted_20_white.png');
downSkin: Embed('assets/images/audio_muted_20_white.png');
disabledSkin: Embed('assets/images/audio_muted_20_white.png');
}
.resizeHndlr
{
@ -782,12 +838,12 @@ MDIWindow { /*None of the following properties are overridden by the MDIWindow c
}
Alert {
borderColor: #DFDFDF;
backgroundColor: #EFEFEF;
borderAlpha: 1;
shadowDistance: 1;
dropShadowColor: #FFFFFF;
color: #000000;
borderColor: #DFDFDF;
backgroundColor: #EFEFEF;
borderAlpha: 1;
shadowDistance: 1;
dropShadowColor: #FFFFFF;
color: #000000;
}
.lockSettingsDefaultLabelStyle {
@ -851,4 +907,4 @@ Alert {
.statusMessageStyle {
fontSize: 12;
paddingTop: 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

View File

@ -428,6 +428,7 @@
<copy file="${PROD_RESOURCES_DIR}/expressInstall.swf" todir="${OUTPUT_DIR}" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/example-info-data.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/layout.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
<copy file="${PROD_RESOURCES_DIR}/profiles.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
<if>
<equals arg1="${BUILD_ENV}" arg2="DEV"/>
<then>

View File

@ -247,7 +247,7 @@ bbb.video.publish.hint.waitingApproval = Waiting for approval
bbb.video.publish.hint.videoPreview = Webcam preview
bbb.video.publish.hint.openingCamera = Opening webcam...
bbb.video.publish.hint.cameraDenied = Webcam access denied
bbb.video.publish.hint.cameraIsBeingUsed = Your webcam is being used by another application
bbb.video.publish.hint.cameraIsBeingUsed = Your webcam couldn't be opened - it may be under use by another application
bbb.video.publish.hint.publishing = Publishing...
bbb.video.publish.closeBtn.accessName = Close the webcam settings dialog box
bbb.video.publish.closeBtn.label = Cancel

View File

@ -247,7 +247,7 @@ bbb.video.publish.hint.waitingApproval = Aguardando permissão
bbb.video.publish.hint.videoPreview = Preview do vídeo
bbb.video.publish.hint.openingCamera = Abrindo câmera...
bbb.video.publish.hint.cameraDenied = Acesso à câmera negado
bbb.video.publish.hint.cameraIsBeingUsed = Sua câmera está sendo usada por outro aplicativo
bbb.video.publish.hint.cameraIsBeingUsed = Sua câmera não pôde ser aberta - ela pode estar sendo usada por outro aplicativo
bbb.video.publish.hint.publishing = Transmitindo vídeo...
bbb.video.publish.closeBtn.accessName = Fechar caixa de diálogo de configuração da webcam
bbb.video.publish.closeBtn.label = Cancelar

View File

@ -66,10 +66,8 @@
<module name="VideoconfModule" url="http://HOST/client/VideoconfModule.swf?v=VERSION"
uri="rtmp://HOST/video"
dependson = "UsersModule"
videoQuality = "100"
presenterShareOnly = "false"
controlsForPresenter = "false"
resolutions = "320x240,640x480,1280x720"
autoStart = "false"
skipCamSettingsCheck="false"
showButton = "true"
@ -77,22 +75,16 @@
publishWindowVisible = "true"
viewerWindowMaxed = "false"
viewerWindowLocation = "top"
camKeyFrameInterval = "30"
camModeFps = "10"
camQualityBandwidth = "0"
camQualityPicture = "90"
smoothVideo="false"
applyConvolutionFilter="false"
convolutionFilter="-1, 0, -1, 0, 6, 0, -1, 0, -1"
filterBias="0"
filterDivisor="4"
enableH264 = "true"
h264Level = "2.1"
h264Profile = "main"
displayAvatar = "false"
focusTalking = "false"
glowColor = "0x4A931D"
glowBlurSize = "30.0"
glowBlurSize = "30.0"
priorityRatio = "0.67"
/>
<module name="WhiteboardModule" url="http://HOST/client/WhiteboardModule.swf?v=4105"
@ -121,6 +113,7 @@
maxFileSize="30"
/>
<!--
<module name="VideodockModule" url="http://HOST/client/VideodockModule.swf?v=VERSION"
uri="rtmp://HOST/bigbluebutton"
dependsOn="VideoconfModule, UsersModule"
@ -134,7 +127,8 @@
oneAlwaysBigger="false"
baseTabIndex="401"
/>
-->
<module name="LayoutModule" url="http://HOST/client/LayoutModule.swf?v=VERSION"
uri="rtmp://HOST/bigbluebutton"
layoutConfig="http://HOST/client/conf/layout.xml"

View File

@ -0,0 +1,73 @@
<?xml version="1.0"?>
<profiles fallbackLocale="en_US">
<profile id="hd">
<locale>
<en_US>High definition</en_US>
<pt_BR>Alta definição</pt_BR>
</locale>
<width>1280</width>
<height>720</height>
<keyFrameInterval>5</keyFrameInterval>
<modeFps>15</modeFps>
<qualityBandwidth>0</qualityBandwidth>
<qualityPicture>90</qualityPicture>
<enableH264>true</enableH264>
<h264Level>2.1</h264Level>
<h264Profile>main</h264Profile>
</profile>
<profile id="vga">
<locale>
<en_US>VGA</en_US>
</locale>
<width>640</width>
<height>480</height>
<keyFrameInterval>5</keyFrameInterval>
<modeFps>10</modeFps>
<qualityBandwidth>0</qualityBandwidth>
<qualityPicture>90</qualityPicture>
<enableH264>true</enableH264>
<h264Level>2.1</h264Level>
<h264Profile>main</h264Profile>
</profile>
<profile id="low1" default="true">
<locale>
<en_US>Low bandwidth</en_US>
<pt_BR>Baixa banda</pt_BR>
</locale>
<width>320</width>
<height>240</height>
<keyFrameInterval>5</keyFrameInterval>
<modeFps>10</modeFps>
<qualityBandwidth>0</qualityBandwidth>
<qualityPicture>90</qualityPicture>
<enableH264>true</enableH264>
<h264Level>2.1</h264Level>
<h264Profile>main</h264Profile>
</profile>
<profile id="low2">
<locale>
<en_US>Low latency + low CPU</en_US>
<pt_BR>Baixa banda + baixo CPU</pt_BR>
</locale>
<width>320</width>
<height>240</height>
<keyFrameInterval>5</keyFrameInterval>
<modeFps>5</modeFps>
<qualityBandwidth>0</qualityBandwidth>
<qualityPicture>90</qualityPicture>
<enableH264>false</enableH264>
</profile>
<profile id="pic">
<locale>
<en_US>One frame per second</en_US>
<pt_BR>Um quadro por segundo</pt_BR>
</locale>
<width>320</width>
<height>240</height>
<keyFrameInterval>5</keyFrameInterval>
<modeFps>1</modeFps>
<qualityBandwidth>0</qualityBandwidth>
<qualityPicture>90</qualityPicture>
<enableH264>false</enableH264>
</profile>
</profiles>

View File

@ -72,6 +72,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
setupAPI();
EventBroadcaster.getInstance().addEventListener("configLoadedEvent", configLoadedEventHandler);
BBB.initConfigManager();
BBB.initVideoProfileManager();
globalModifier = ExternalInterface.call("determineGlobalModifier");
}

View File

@ -70,14 +70,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
return _attributes.userrole as String;
}
public function get quality():Number{
return Number(_attributes.videoQuality);
}
public function get resolutions():String {
return _attributes.resolutions;
}
public function get presenterShareOnly():Boolean{
if (_attributes.presenterShareOnly == "true") return true;
else return false;

View File

@ -306,5 +306,11 @@ package org.bigbluebutton.common
[Embed(source="assets/images/grid_icon.png")]
public var grid_icon:Class;
[Embed(source="assets/images/moderator_white.png")]
public var moderator_white:Class;
[Embed(source="assets/images/presenter_white.png")]
public var presenter_white:Class;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

View File

@ -22,7 +22,9 @@ package org.bigbluebutton.core
import org.bigbluebutton.core.managers.ConnectionManager;
import org.bigbluebutton.core.managers.UserConfigManager;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.core.managers.VideoProfileManager;
import org.bigbluebutton.core.model.Session;
import org.bigbluebutton.core.model.VideoProfile;
import flash.system.Capabilities;
public class BBB {
@ -30,6 +32,7 @@ package org.bigbluebutton.core
private static var connectionManager:ConnectionManager = null;
private static var session:Session = null;
private static var userConfigManager:UserConfigManager = null;
private static var videoProfileManager:VideoProfileManager = null;
public static function initUserConfigManager():UserConfigManager {
if (userConfigManager == null) {
@ -46,10 +49,34 @@ package org.bigbluebutton.core
return configManager;
}
public static function initVideoProfileManager():VideoProfileManager {
if (videoProfileManager == null) {
videoProfileManager = new VideoProfileManager();
videoProfileManager.loadProfiles();
}
return videoProfileManager;
}
public static function getConfigForModule(module:String):XML {
return initConfigManager().config.getConfigFor(module);
}
public static function get videoProfiles():Array {
return initVideoProfileManager().profiles;
}
public static function getVideoProfileById(id:String):VideoProfile {
return initVideoProfileManager().getVideoProfileById(id);
}
public static function get defaultVideoProfile():VideoProfile {
return initVideoProfileManager().defaultVideoProfile;
}
public static function get fallbackVideoProfile():VideoProfile {
return initVideoProfileManager().fallbackVideoProfile;
}
public static function initConnectionManager():ConnectionManager {
if (connectionManager == null) {
connectionManager = new ConnectionManager();

View File

@ -76,10 +76,10 @@ package org.bigbluebutton.core
return false;
}
public static function getWebcamStream(userID:String):String {
public static function getWebcamStream(userID:String):Array {
var u:BBBUser = getUser(userID);
if (u != null && u.hasStream) {
return u.streamName;
return u.streamNames;
}
return null;
@ -179,4 +179,4 @@ package org.bigbluebutton.core
}
}
}
}

View File

@ -0,0 +1,119 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 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 3.0 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.core.managers
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.core.FlexGlobals;
import mx.utils.URLUtil;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.core.EventBroadcaster;
import org.bigbluebutton.core.model.VideoProfile;
public class VideoProfileManager extends EventDispatcher {
public static const PROFILES_XML:String = "client/conf/profiles.xml";
public static const DEFAULT_FALLBACK_LOCALE:String = "en_US";
private var _profiles:Array = new Array();
public function loadProfiles():void {
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, handleComplete);
var date:Date = new Date();
var localeReqURL:String = buildRequestURL() + "?a=" + date.time;
trace("VideoProfileManager::loadProfiles [" + localeReqURL + "]");
urlLoader.load(new URLRequest(localeReqURL));
}
private function buildRequestURL():String {
var swfURL:String = FlexGlobals.topLevelApplication.url;
var protocol:String = URLUtil.getProtocol(swfURL);
var serverName:String = URLUtil.getServerNameWithPort(swfURL);
return protocol + "://" + serverName + "/" + PROFILES_XML;
}
private function handleComplete(e:Event):void{
trace("VideoProfileManager::handleComplete [" + new XML(e.target.data) + "]");
// first clear the array
_profiles.splice(0);
var profiles:XML = new XML(e.target.data);
var fallbackLocale:String = profiles.@fallbackLocale != undefined? profiles.@fallbackLocale.toString(): DEFAULT_FALLBACK_LOCALE;
for each (var profile:XML in profiles.children()) {
_profiles.push(new VideoProfile(profile, fallbackLocale));
}
}
public function get profiles():Array {
if (_profiles.length > 0) {
return _profiles;
} else {
return [ fallbackVideoProfile ];
}
}
public function getVideoProfileById(id:String):VideoProfile {
for each (var profile:VideoProfile in _profiles) {
if (profile.id == id) {
return profile;
}
}
return null;
}
public function get defaultVideoProfile():VideoProfile {
for each (var profile:VideoProfile in _profiles) {
if (profile.defaultProfile) {
return profile;
}
}
if (_profiles.length > 0) {
return _profiles[0];
} else {
return null;
}
}
public function get fallbackVideoProfile():VideoProfile {
return new VideoProfile(
<profile id="4L7344ZoBYGTocbHOIvzXsrGiBGoohFv" default="true">
<locale>
<en_US>Fallback profile</en_US>
</locale>
<width>320</width>
<height>240</height>
<keyFrameInterval>5</keyFrameInterval>
<modeFps>10</modeFps>
<qualityBandwidth>0</qualityBandwidth>
<qualityPicture>90</qualityPicture>
<enableH264>true</enableH264>
<h264Level>2.1</h264Level>
<h264Profile>main</h264Profile>
</profile>
, DEFAULT_FALLBACK_LOCALE);
}
}
}

View File

@ -0,0 +1,168 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 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 3.0 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.core.model
{
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.util.i18n.ResourceUtil;
import flash.utils.Dictionary;
public class VideoProfile
{
private var _fallbackLanguage:String;
private static var _nextId:int = -1;
private var _id:String;
private var _default:Boolean = false;
private var _name:Dictionary = new Dictionary();
private var _width:int = 320;
private var _height:int = 240;
private var _keyFrameInterval:int = 30;
private var _modeFps:int = 10;
private var _qualityBandwidth:int = 0;
private var _qualityPicture:int = 90;
private var _enableH264:Boolean = true;
private var _h264Level:String = "2.1";
private var _h264Profile:String = "main";
public function VideoProfile(vxml:XML, fallbackLanguage:String)
{
_fallbackLanguage = fallbackLanguage;
if (vxml.@id != undefined) {
_id = vxml.@id.toString();
} else {
_id = String(nextId());
}
if (vxml.@default != undefined) {
_default = (vxml.@default.toString().toUpperCase() == "TRUE") ? true : false;
}
if (vxml.locale != undefined) {
for each (var locale:XML in vxml.locale.children()) {
_name[locale.localName()] = locale.toString();
}
}
if (vxml.width != undefined) {
_width = vxml.width;
}
if (vxml.height != undefined) {
_height = vxml.height;
}
if (vxml.keyFrameInterval != undefined) {
_keyFrameInterval = vxml.keyFrameInterval;
}
if (vxml.modeFps != undefined) {
_modeFps = vxml.modeFps;
}
if (vxml.qualityBandwidth != undefined) {
_qualityBandwidth = vxml.qualityBandwidth;
}
if (vxml.qualityPicture != undefined) {
_qualityPicture = vxml.qualityPicture;
}
if (vxml.enableH264 != undefined) {
_enableH264 = (vxml.enableH264.toString().toUpperCase() == "TRUE") ? true : false;
}
if (vxml.h264Level != undefined) {
_h264Level = vxml.h264Level.toString();
}
if (vxml.h264Profile != undefined) {
_h264Profile = vxml.h264Profile.toString();
}
trace("This is a new video profile");
trace(this.toString());
}
public function toString():String {
return "VideoProfile [ "
+ "id: " + this.id + ", "
+ "default: " + this.defaultProfile + ", "
+ "name: " + this.name + ", "
+ "width: " + this.width + ", "
+ "height: " + this.height + ", "
+ "keyFrameInterval: " + this.keyFrameInterval + ", "
+ "modeFps: " + this.modeFps + ", "
+ "qualityBandwidth: " + this.qualityBandwidth + ", "
+ "qualityPicture: " + this.qualityPicture + ", "
+ "enableH264: " + this.enableH264 + ", "
+ "h264Level: " + this.h264Level + ", "
+ "h264Profile: " + this.h264Profile + " ]";
}
private static function nextId():int {
_nextId++;
return _nextId;
}
public function get id():String {
return _id;
}
public function get defaultProfile():Boolean {
return _default;
}
public function get name():String {
var locale:String = ResourceUtil.getInstance().getCurrentLanguageCode();
if (_name.hasOwnProperty(locale)) {
return _name[locale];
} else if (_name.hasOwnProperty(_fallbackLanguage)) {
return _name[_fallbackLanguage];
} else {
return "";
}
}
public function get width():int {
return _width;
}
public function get height():int {
return _height;
}
public function get keyFrameInterval():int {
return _keyFrameInterval;
}
public function get modeFps():int {
return _modeFps;
}
public function get qualityBandwidth():int {
return _qualityBandwidth;
}
public function get qualityPicture():int {
return _qualityPicture;
}
public function get enableH264():Boolean {
return _enableH264;
}
public function get h264Level():String {
return _h264Level;
}
public function get h264Profile():String {
return _h264Profile;
}
}
}

View File

@ -18,11 +18,12 @@
*/
package org.bigbluebutton.core.vo
{
import org.bigbluebutton.core.model.VideoProfile;
public class CameraSettingsVO
{
public var camIndex:int = 0;
public var camWidth:int = 0;
public var camHeight:int = 0;
public var videoProfile:VideoProfile = null;
public var isPublishing:Boolean = false;
}

View File

@ -161,8 +161,8 @@ package org.bigbluebutton.main.api
var obj:Object = new Object();
var isUserPublishing:Boolean = false;
var streamName:String = UsersUtil.getWebcamStream(UsersUtil.externalUserIDToInternalUserID(userID));
if (streamName != null) {
var streamNames:Array = UsersUtil.getWebcamStream(UsersUtil.externalUserIDToInternalUserID(userID));
if (streamNames && streamNames.length > 0) {
isUserPublishing = true;
}
@ -170,7 +170,7 @@ package org.bigbluebutton.main.api
obj.uri = vidConf.uri + "/" + UsersUtil.getInternalMeetingID();
obj.userID = userID;
obj.isUserPublishing = isUserPublishing;
obj.streamName = streamName;
obj.streamNames = streamNames;
obj.avatarURL = UsersUtil.getAvatarURL();
return obj;
@ -200,15 +200,12 @@ package org.bigbluebutton.main.api
var camSettings:CameraSettingsVO = UsersUtil.amIPublishing();
obj.isPublishing = camSettings.isPublishing;
obj.camIndex = camSettings.camIndex;
obj.camWidth = camSettings.camWidth;
obj.camHeight = camSettings.camHeight;
var vidConf:VideoConfOptions = new VideoConfOptions();
obj.camKeyFrameInterval = vidConf.camKeyFrameInterval;
obj.camModeFps = vidConf.camModeFps;
obj.camQualityBandwidth = vidConf.camQualityBandwidth;
obj.camQualityPicture = vidConf.camQualityPicture;
obj.camWidth = camSettings.videoProfile.width;
obj.camHeight = camSettings.videoProfile.height;
obj.camKeyFrameInterval = camSettings.videoProfile.keyFrameInterval;
obj.camModeFps = camSettings.videoProfile.modeFps;
obj.camQualityBandwidth = camSettings.videoProfile.qualityBandwidth;
obj.camQualityPicture = camSettings.videoProfile.qualityPicture;
obj.avatarURL = UsersUtil.getAvatarURL();
return obj;

View File

@ -77,8 +77,8 @@ package org.bigbluebutton.main.api
var payload:Object = new Object();
var isUserPublishing:Boolean = false;
var streamName:String = UsersUtil.getWebcamStream(event.userID);
if (streamName != null) {
var streamNames:Array = UsersUtil.getWebcamStream(event.userID);
if (streamNames && streamNames.length > 0) {
isUserPublishing = true;
}
@ -89,7 +89,7 @@ package org.bigbluebutton.main.api
var vidConf:VideoConfOptions = new VideoConfOptions();
payload.uri = vidConf.uri + "/" + UsersUtil.getInternalMeetingID();
payload.avatarURL = UsersUtil.getAvatarURL();
payload.streamName = streamName;
payload.streamNames = streamNames;
broadcastEvent(payload);
}
@ -141,20 +141,18 @@ package org.bigbluebutton.main.api
}
public function handleBroadcastStartedEvent(event:BroadcastStartedEvent):void {
var vidConf:VideoConfOptions = new VideoConfOptions();
var payload:Object = new Object();
payload.eventName = EventConstants.BROADCASTING_CAM_STARTED;
payload.isPresenter = UsersUtil.amIPresenter();
payload.streamName = event.stream;
payload.isPublishing = event.camSettings.isPublishing;
payload.camIndex = event.camSettings.camIndex;
payload.camWidth = event.camSettings.camWidth;
payload.camHeight = event.camSettings.camHeight;
payload.camKeyFrameInterval = vidConf.camKeyFrameInterval;
payload.camModeFps = vidConf.camModeFps;
payload.camQualityBandwidth = vidConf.camQualityBandwidth;
payload.camQualityPicture = vidConf.camQualityPicture;
payload.camWidth = event.camSettings.videoProfile.width;
payload.camHeight = event.camSettings.videoProfile.height;
payload.camKeyFrameInterval = event.camSettings.videoProfile.keyFrameInterval;
payload.camModeFps = event.camSettings.videoProfile.modeFps;
payload.camQualityBandwidth = event.camSettings.videoProfile.qualityBandwidth;
payload.camQualityPicture = event.camSettings.videoProfile.qualityPicture;
payload.avatarURL = UsersUtil.getAvatarURL();
broadcastEvent(payload);
@ -162,18 +160,17 @@ package org.bigbluebutton.main.api
public function handleAmISharingCamQueryEvent(event:AmISharingWebcamQueryEvent):void {
var camSettings:CameraSettingsVO = UsersUtil.amIPublishing();
var vidConf:VideoConfOptions = new VideoConfOptions();
var payload:Object = new Object();
payload.eventName = EventConstants.AM_I_SHARING_CAM_RESP;
payload.isPublishing = camSettings.isPublishing;
payload.camIndex = camSettings.camIndex;
payload.camWidth = camSettings.camWidth;
payload.camHeight = camSettings.camHeight;
payload.camKeyFrameInterval = vidConf.camKeyFrameInterval;
payload.camModeFps = vidConf.camModeFps;
payload.camQualityBandwidth = vidConf.camQualityBandwidth;
payload.camQualityPicture = vidConf.camQualityPicture;
payload.camWidth = camSettings.videoProfile.width;
payload.camHeight = camSettings.videoProfile.height;
payload.camKeyFrameInterval = camSettings.videoProfile.keyFrameInterval;
payload.camModeFps = camSettings.videoProfile.modeFps;
payload.camQualityBandwidth = camSettings.videoProfile.qualityBandwidth;
payload.camQualityPicture = camSettings.videoProfile.qualityPicture;
payload.avatarURL = UsersUtil.getAvatarURL();
broadcastEvent(payload);
@ -413,4 +410,4 @@ package org.bigbluebutton.main.api
}
}
}
}
}

View File

@ -27,9 +27,12 @@ package org.bigbluebutton.main.events
// The userID of the webcam being viewed.
public var webcamUserID:String;
// The streamName of the user
public var streamName:String;
public function StoppedViewingWebcamEvent(bubbles:Boolean=true, cancelable:Boolean=false)
{
super(STOPPED_VIEWING_WEBCAM, bubbles, cancelable);
}
}
}
}

View File

@ -52,20 +52,79 @@ package org.bigbluebutton.main.model.users
[Bindable] public var disableMyPublicChat:Boolean = false;
[Bindable] public var lockedLayout:Boolean = false;
private var _hasStream:Boolean = false;
[Bindable]
public function get hasStream():Boolean {
return _hasStream;
return streamNames.length > 0;
}
public function set hasStream(s:Boolean):void {
_hasStream = s;
verifyMedia();
throw new Error("hasStream cannot be set. It is derived directly from streamName");
}
[Bindable] public var viewingStream:Boolean = false;
[Bindable] public var streamName:String = "";
[Bindable] private var _viewingStream:Array = new Array();
[Bindable]
public function get viewingStream():Array {
return _viewingStream;
}
public function set viewingStream(v:Array):void {
throw new Error("Please use the helpers addViewingStream or removeViewingStream to handle viewingStream");
}
public function addViewingStream(streamName:String):Boolean {
trace("Before adding the stream " + streamName + ": " + _viewingStream);
if (isViewingStream(streamName)) {
return false;
}
_viewingStream.push(streamName);
trace("After adding the stream " + streamName + ": " + _viewingStream);
return true;
}
public function removeViewingStream(streamName:String):Boolean {
trace("Before removing the stream " + streamName + ": " + _viewingStream);
if (!isViewingStream(streamName)) {
return false;
}
_viewingStream = _viewingStream.filter(function(item:*, index:int, array:Array):Boolean { return item != streamName; });
trace("After removing the stream " + streamName + ": " + _viewingStream);
return true;
}
private function isViewingStream(streamName:String):Boolean {
return _viewingStream.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; });
}
public function isViewingAllStreams():Boolean {
return _viewingStream.length == streamNames.length;
}
[Bindable] public var streamNames:Array = new Array();
[Bindable]
public function get streamName():String {
var streams:String = "";
for each(var stream:String in streamNames) {
streams = streams + stream + "|";
}
//Remove last |
streams = streams.slice(0, streams.length-1);
return streams;
}
private function hasThisStream(streamName:String):Boolean {
return streamNames.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; });
}
public function set streamName(name:String):void {
if(name && name != "") {
var names:Array = name.split("|");
for each(var stream:String in names) {
if(stream != "" && !hasThisStream(name)) {
streamNames.push(name);
sendStreamStartedEvent(name);
}
}
}
}
private var _presenter:Boolean = false;
[Bindable]
public function get presenter():Boolean {
@ -197,16 +256,18 @@ package org.bigbluebutton.main.model.users
}
public function sharedWebcam(stream: String):void {
hasStream = true;
streamName = stream;
if (hasStream) sendStreamStartedEvent();
if(stream && stream != "") {
streamNames.push(stream);
sendStreamStartedEvent(stream);
}
buildStatus();
verifyMedia();
}
public function unsharedWebcam():void {
hasStream = false;
streamName = "";
public function unsharedWebcam(stream: String):void {
streamNames = streamNames.filter(function(item:*, index:int, array:Array):Boolean { return item != stream });
buildStatus();
verifyMedia();
}
public function presenterStatusChanged(presenter: Boolean):void {
@ -244,15 +305,8 @@ package org.bigbluebutton.main.model.users
*
* hasStream = new Boolean(String(streamInfo[0]));
*/
if (String(streamInfo[0]).toUpperCase() == "TRUE") {
hasStream = true;
} else {
hasStream = false;
}
var streamNameInfo:Array = String(streamInfo[1]).split(/=/);
streamName = streamNameInfo[1];
if (hasStream) sendStreamStartedEvent();
break;
case "raiseHand":
raiseHand = status.value as Boolean;
@ -279,9 +333,8 @@ package org.bigbluebutton.main.model.users
n.userID = user.userID;
n.externUserID = user.externUserID;
n.name = user.name;
n.hasStream = user.hasStream;
n.viewingStream = user.viewingStream;
n.streamName = user.streamName;
n._viewingStream = user._viewingStream;
n.streamNames = user.streamNames;
n.presenter = user.presenter;
n.raiseHand = user.raiseHand;
n.role = user.role;
@ -301,9 +354,9 @@ package org.bigbluebutton.main.model.users
return n;
}
private function sendStreamStartedEvent():void{
private function sendStreamStartedEvent(stream: String):void{
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new StreamStartedEvent(userID, name, streamName));
dispatcher.dispatchEvent(new StreamStartedEvent(userID, name, stream));
}
public function applyLockSettings():void {
@ -335,4 +388,4 @@ package org.bigbluebutton.main.model.users
}
}
}
}
}

View File

@ -394,10 +394,10 @@ package org.bigbluebutton.main.model.users {
users.refresh();
}
public function unsharedWebcam(userId: String):void {
var aUser:BBBUser = getUser(userId);
public function unsharedWebcam(userId: String, stream:String):void {
var aUser:BBBUser = getUser(userId);
if (aUser != null) {
aUser.unsharedWebcam()
aUser.unsharedWebcam(stream);
}
users.refresh();

View File

@ -31,7 +31,7 @@ package org.bigbluebutton.main.model.users
import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
import org.bigbluebutton.main.model.ConferenceParameters;
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
import org.bigbluebutton.main.model.users.events.UsersConnectionEvent;
import org.bigbluebutton.main.model.users.events.UsersConnectionEvent;
public class NetConnectionDelegate
{

View File

@ -1,339 +1,240 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:view="org.bigbluebutton.main.views.*"
layout="absolute"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="630" height="450" creationComplete="onCreationComplete()" styleName="cameraDisplaySettingsWindowStyle"
showCloseButton="false" close="onCancelClicked()" keyDown="handleKeyDown(event)">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flash.ui.Keyboard;
import mx.events.CloseEvent;
import mx.events.ItemClickEvent;
import mx.managers.PopUpManager;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
static public var PADDING_HORIZONTAL:Number = 6;
static public var PADDING_VERTICAL:Number = 29;
private var images:Images = new Images();
[Bindable]
private var cancelIcon:Class = images.control_play;
[Bindable]
public var resolutions:Array;
public var publishInClient:Boolean;
public var camWidth:Number = 320;
public var camHeight:Number = 240;
public var chromePermissionDenied:Boolean = false;
private var _camera:Camera = null;
// Timer used to enable the start publishing button, only after get any activity on the camera.
// It avoids the problem of publishing a blank video
private var _activationTimer:Timer = null;
private var _waitingForActivation:Boolean = false;
static private var _cameraAccessDenied:Boolean = false;
private var _video:Video;
private var aspectRatio:Number = 1;
[Bindable]private var baseIndex:int;
override public function move(x:Number, y:Number):void
{
return;
}
private function onCreationComplete():void {
tabIndex = 51;
changeDefaultCamForMac();
if (resolutions.length > 1) {
showResControls(true);
}
if (Camera.names.length > 1) {
showVideoControls(true);
}
updateCamera();
}
private function changeDefaultCamForMac():void {
for (var i:int = 0; i < Camera.names.length; i++){
if (Camera.names[i] == "USB Video Class Video") {
/** Set as default for Macs */
_camera = Camera.getCamera("USB Video Class Video");
}
}
}
private function showVideoControls(show:Boolean):void {
if (show) {
this.visible = true;
btnStartPublish.visible = true;
} else{
this.width = 0;
this.height = 0;
btnStartPublish.visible = false;
this.visible = false;
}
}
private function updateCamera():void {
_camera = null;
_camera = Camera.getCamera();
if (_camera == null) {
_videoHolder.showWarning('bbb.video.publish.hint.cantOpenCamera');
return;
}
_camera.addEventListener(ActivityEvent.ACTIVITY, onActivityEvent);
_camera.addEventListener(StatusEvent.STATUS, onStatusEvent);
if (_camera.muted) {
if (_cameraAccessDenied && !chromePermissionDenied) {
//onCameraAccessDisallowed();
//return;
Security.showSettings(SecurityPanel.PRIVACY)
} else if (chromePermissionDenied) {
_videoHolder.showWarning('bbb.video.publish.hint.cameraDenied');
return;
}else {
_videoHolder.showWarning('bbb.video.publish.hint.waitingApproval');
}
} else {
// if the camera isn't muted, that is because the user has
// previously allowed the camera capture on the flash privacy box
onCameraAccessAllowed();
}
displayVideoPreview();
}
private function displayVideoPreview():void {
setComboResolution();
var videoOptions:VideoConfOptions = new VideoConfOptions();
_camera.setMotionLevel(5, 1000);
_camera.setKeyFrameInterval(videoOptions.camKeyFrameInterval);
_camera.setMode(camWidth, camHeight, videoOptions.camModeFps);
_camera.setQuality(videoOptions.camQualityBandwidth, videoOptions.camQualityPicture);
if (_camera.width != camWidth || _camera.height != camHeight) {
LogUtil.debug("Resolution " + camWidth + "x" + camHeight + " is not supported, using " + _camera.width + "x" + _camera.height + " instead");
setResolution(_camera.width, _camera.height);
}
if (_video != null) {
_videoHolder.remove(_video);
}
_video = new Video();
_video.attachCamera(_camera);
//aspectRatio = (_video.width / _video.height);
if (aspectRatio > _videoHolder.width / _videoHolder.height) {
_video.width = _videoHolder.width;
_video.height = _videoHolder.width / aspectRatio;
_video.x = 0;
_video.y = (_videoHolder.height - _video.height) / 2;
} else {
_video.width = _videoHolder.height * aspectRatio;
_video.height = _videoHolder.height;
_video.x = (_videoHolder.width - _video.width) / 2;
_video.y = 0;
}
_videoHolder.add(_video);
}
private function showResControls(show:Boolean):void {
if (show) cmbResolution.visible = true;
else cmbResolution.visible = false;
}
private function setComboResolution():void {
var res:Array = cmbResolution.selectedLabel.split( "x" );
setResolution(Number(res[0]), Number(res[1]));
}
private function setAspectRatio(width:int, height:int):void {
aspectRatio = (width/height);
this.minHeight = Math.floor((this.minWidth - PADDING_HORIZONTAL) / aspectRatio) + PADDING_VERTICAL;
}
private function setResolution(width:int, height:int):void {
camWidth = width;
camHeight = height;
setAspectRatio(camWidth, camHeight);
}
private function startPublishing():void {
updateCamera();
// Save the index of the camera. Need it to send the message.
var camIndex:int = _camera.index;
disableCamera();
var globalDispatcher:Dispatcher = new Dispatcher();
var camEvent:BBBEvent = new BBBEvent(BBBEvent.CAMERA_SETTING);
camEvent.payload.cameraIndex = camIndex;
camEvent.payload.cameraWidth = camWidth;
camEvent.payload.cameraHeight = camHeight;
camEvent.payload.publishInClient = publishInClient;
globalDispatcher.dispatchEvent(camEvent);
var event:BBBEvent = new BBBEvent(BBBEvent.CAM_SETTINGS_CLOSED);
event.payload['clicked'] = "close";
dispatchEvent(event);
PopUpManager.removePopUp(this);
}
private function disableCamera():void {
_camera = null;
if(_video != null){
_video.attachCamera(null);
_video.clear();
_videoHolder.remove(_video);
_video = null;
}
}
private function handleKeyDown(event:KeyboardEvent):void {
if (event.charCode == Keyboard.ESCAPE) {
disableCamera();
this.dispatchEvent(new CloseEvent(CloseEvent.CLOSE));
}
}
private function onCancelClicked():void {
disableCamera();
var event:BBBEvent = new BBBEvent(BBBEvent.CAM_SETTINGS_CLOSED);
event.payload['clicked'] = "cancel";
dispatchEvent(event);
PopUpManager.removePopUp(this);
}
private function onActivityEvent(e:ActivityEvent):void {
if (_waitingForActivation && e.activating) {
_activationTimer.stop();
_videoHolder.showWarning('bbb.video.publish.hint.videoPreview', false, "0xFFFF00");
// controls.btnStartPublish.enabled = true;
_waitingForActivation = false;
btnStartPublish.enabled = true;
}
}
private function onStatusEvent(e:StatusEvent):void {
if (e.code == "Camera.Unmuted") {
onCameraAccessAllowed();
// this is just to overwrite the message of waiting for approval
_videoHolder.showWarning('bbb.video.publish.hint.openingCamera');
} else {//if (e.code == "Camera.Muted") {
onCameraAccessDisallowed();
}
}
private function onCameraAccessAllowed():void {
_cameraAccessDenied = false;
// set timer to ensure that the camera activates. If not, it might be in use by another application
_waitingForActivation = true;
if (_activationTimer != null) {
_activationTimer.stop();
}
_activationTimer = new Timer(10000, 1);
_activationTimer.addEventListener(TimerEvent.TIMER, activationTimeout);
_activationTimer.start();
}
private function onCameraAccessDisallowed():void {
_videoHolder.showWarning('bbb.video.publish.hint.cameraDenied');
_cameraAccessDenied = true;
}
private function activationTimeout(e:TimerEvent):void {
_videoHolder.showWarning('bbb.video.publish.hint.cameraIsBeingUsed');
}
private function showCameraSettings():void {
Security.showSettings(SecurityPanel.CAMERA);
}
]]>
</mx:Script>
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:view="org.bigbluebutton.main.views.*"
layout="absolute"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="630" height="450" creationComplete="onCreationComplete()" styleName="cameraDisplaySettingsWindowStyle"
showCloseButton="false" close="onCancelClicked()" keyDown="handleKeyDown(event)">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flash.ui.Keyboard;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.events.CloseEvent;
import mx.events.ItemClickEvent;
import mx.managers.PopUpManager;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
static public var PADDING_HORIZONTAL:Number = 6;
static public var PADDING_VERTICAL:Number = 29;
private var images:Images = new Images();
[Bindable]
private var cancelIcon:Class = images.control_play;
[Bindable]
public var _videoProfiles:ArrayCollection = new ArrayCollection();
public var selectedVideoProfile:VideoProfile;
public var publishInClient:Boolean;
public var defaultCamera:String = null;
public var camerasArray:Object;
[Bindable] private var camerasAvailable:ArrayList = new ArrayList();
public var chromePermissionDenied:Boolean = false;
public const OFF_STATE:Number = 0;
public const ON_STATE:Number = 1;
private var selectedCam:int;
private var aspectRatio:Number = 1;
[Bindable]private var baseIndex:int;
override public function move(x:Number, y:Number):void
{
return;
}
private function onCreationComplete():void {
tabIndex = 51;
if(defaultCamera != null) {
var indexDefault:int = 0;
for (var i:int = 0; i < Camera.names.length; i++){
if(camerasArray[i].status == OFF_STATE) {
var myObj:Object = {}
myObj.label = camerasArray[i].label;
myObj.index = String(i);
camerasAvailable.addItem(myObj);
if(myObj.index == defaultCamera)
indexDefault = camerasAvailable.length-1;
}
}
cmbCameraSelector.selectedIndex = indexDefault;
defaultCamera = null;
} else {
cmbCameraSelector.selectedIndex = 0;
}
var idx:int = 0;
var defaultProfile:VideoProfile = BBB.defaultVideoProfile;
for each (var value:VideoProfile in BBB.videoProfiles) {
var item:Object = {index:idx, label:value.name, profile:value};
_videoProfiles.addItem(item);
if (value.id == defaultProfile.id) {
cmbVideoProfile.selectedIndex = idx;
}
idx++;
}
if (_videoProfiles.length > 1) {
showResControls(true);
}
if (Camera.names.length > 1) {
showVideoControls(true);
}
updateCamera();
}
private function showVideoControls(show:Boolean):void {
if (show) {
this.visible = true;
btnStartPublish.visible = true;
} else{
this.width = 0;
this.height = 0;
btnStartPublish.visible = false;
this.visible = false;
}
}
private function updateCamera():void {
selectedVideoProfile = cmbVideoProfile.selectedItem.profile as VideoProfile;
if (camerasAvailable.length > cmbCameraSelector.selectedIndex) {
selectedCam = camerasAvailable.getItemAt(cmbCameraSelector.selectedIndex).index;
} else {
selectedCam = -1;
}
setAspectRatio(selectedVideoProfile.width,selectedVideoProfile.height);
_video.successCallback = function():void {
btnStartPublish.enabled = true;
}
_video.chromePermissionDenied = chromePermissionDenied;
_video.updateCamera(selectedCam,selectedVideoProfile,_canvas.width, _canvas.height,true);
}
private function showResControls(show:Boolean):void {
if (show) cmbVideoProfile.visible = true;
else cmbVideoProfile.visible = false;
}
private function setAspectRatio(width:int, height:int):void {
aspectRatio = (width/height);
this.minHeight = Math.floor((this.minWidth - PADDING_HORIZONTAL) / aspectRatio) + PADDING_VERTICAL;
}
private function startPublishing():void {
updateCamera();
disableCamera();
var globalDispatcher:Dispatcher = new Dispatcher();
var camEvent:BBBEvent = new BBBEvent(BBBEvent.CAMERA_SETTING);
camEvent.payload.cameraIndex = selectedCam;
camEvent.payload.videoProfile = selectedVideoProfile;
camEvent.payload.publishInClient = publishInClient;
globalDispatcher.dispatchEvent(camEvent);
var event:BBBEvent = new BBBEvent(BBBEvent.CAM_SETTINGS_CLOSED);
event.payload['clicked'] = "close";
dispatchEvent(event);
PopUpManager.removePopUp(this);
}
private function disableCamera():void {
if(_video != null){
_video.disableCamera();
}
}
private function handleKeyDown(event:KeyboardEvent):void {
if (event.charCode == Keyboard.ESCAPE) {
disableCamera();
this.dispatchEvent(new CloseEvent(CloseEvent.CLOSE));
}
}
private function onCancelClicked():void {
disableCamera();
var event:BBBEvent = new BBBEvent(BBBEvent.CAM_SETTINGS_CLOSED);
event.payload['clicked'] = "cancel";
dispatchEvent(event);
PopUpManager.removePopUp(this);
}
private function showCameraSettings():void {
Security.showSettings(SecurityPanel.CAMERA);
}
]]>
</mx:Script>
<mx:VBox id="webcamDisplay" width="100%" height="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5" styleName="cameraDisplaySettingsWindowBackground">
<mx:HBox width="100%" horizontalAlign="left">
<mx:TextArea width="100%" wordWrap="false" borderSkin="{null}" editable="false" text="{ResourceUtil.getInstance().getString('bbb.users.settings.webcamSettings')}"
styleName="webcamSettingsWindowTitleStyle" tabIndex="{baseIndex}"/>
</mx:HBox>
<mx:HRule width="100%"/>
<mx:Spacer height="1"/>
<view:VideoHolder id="_videoHolder" width="100%" height="75%" />
<mx:HBox width="100%" height="10%" horizontalAlign="center" horizontalGap="13" paddingRight="5">
<mx:Button id="changeCamera" styleName="cameraDisplaySettingsWindowChangeCamBtn"
label="{ResourceUtil.getInstance().getString('bbb.publishVideo.changeCameraBtn.labelText')}"
toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.changeCameraBtn.toolTip')}"
click="showCameraSettings()" tabIndex="{baseIndex+1}"/>
<mx:ComboBox id="cmbResolution" styleName="cameraDisplaySettingsWindowChangeResolutionCombo"
dataProvider="{resolutions}" visible="false" change="updateCamera()" tabIndex="{baseIndex+2}"
toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.cmbResolution.tooltip')}" height="30" />
</mx:HBox>
<mx:HRule width="100%"/>
<mx:HBox width="100%" height="10%" horizontalAlign="right" horizontalGap="13" paddingRight="5" paddingBottom="5" paddingTop="1">
<mx:Button id="btnStartPublish" toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.toolTip')}"
click="startPublishing()" enabled="false" styleName="cameraDisplaySettingsWindowStartBtn"
label="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.labelText')}" tabIndex="{baseIndex+3}" />
<mx:Button id="btnClosePublish"
click="onCancelClicked()"
enabled="true" tabIndex="{baseIndex+4}"
label="{ResourceUtil.getInstance().getString('bbb.video.publish.closeBtn.label')}"
accessibilityName="{ResourceUtil.getInstance().getString('bbb.video.publish.closeBtn.accessName')}"/>
</mx:HBox>
</mx:VBox>
<mx:VBox id="webcamDisplay" width="100%" height="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5" styleName="cameraDisplaySettingsWindowBackground">
<mx:HBox width="100%" horizontalAlign="left">
<mx:TextArea width="100%" wordWrap="false" borderSkin="{null}" editable="false" text="{ResourceUtil.getInstance().getString('bbb.users.settings.webcamSettings')}"
styleName="webcamSettingsWindowTitleStyle" tabIndex="{baseIndex}"/>
</mx:HBox>
<mx:HRule width="100%"/>
<mx:Spacer height="1"/>
<mx:Box id="_canvas" width="100%" height="75%" horizontalAlign="center" verticalAlign="middle">
<view:VideoWithWarnings id="_video"/>
</mx:Box>
<mx:HBox width="100%" height="10%" horizontalAlign="center" horizontalGap="13" paddingRight="5">
<mx:ComboBox id="cmbCameraSelector" styleName="cameraDisplaySettingsWindowCameraSelector" dataProvider="{camerasAvailable}" width="150" visible="true" labelField="label" change="updateCamera()" tabIndex="{baseIndex+1}" height="30"/>
<mx:ComboBox id="cmbVideoProfile" styleName="cameraDisplaySettingsWindowProfileComboStyle"
dataProvider="{_videoProfiles}" visible="false" change="updateCamera()" tabIndex="{baseIndex+2}"
toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.cmbResolution.tooltip')}" height="30" />
</mx:HBox>
<mx:HRule width="100%"/>
<mx:HBox width="100%" height="10%" horizontalAlign="right" horizontalGap="13" paddingRight="5" paddingBottom="5" paddingTop="1">
<mx:Button id="btnStartPublish" toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.toolTip')}"
click="startPublishing()" enabled="false" styleName="cameraDisplaySettingsWindowStartBtn"
label="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.labelText')}" tabIndex="{baseIndex+3}" />
<mx:Button id="btnClosePublish"
click="onCancelClicked()"
enabled="true" tabIndex="{baseIndex+4}"
label="{ResourceUtil.getInstance().getString('bbb.video.publish.closeBtn.label')}"
accessibilityName="{ResourceUtil.getInstance().getString('bbb.video.publish.closeBtn.accessName')}"/>
</mx:HBox>
</mx:VBox>
</mx:TitleWindow>

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.util.i18n.ResourceUtil;
private var hideWarningTimer:Timer = null;
private function onCreationComplete():void {
this.addChild(_videoHolder);
}
public function remove(video:Video):void {
_videoHolder.removeChild(video);
}
public function add(video:Video):void {
_videoHolder.addChild(video);
}
public function showWarning(resourceName:String, autoHide:Boolean=false, color:String="0xFF0000"):void {
const text:String = ResourceUtil.getInstance().getString(resourceName);
if (hideWarningTimer != null) {
hideWarningTimer.stop();
}
if (autoHide) {
hideWarningTimer = new Timer(3000, 1);
hideWarningTimer.addEventListener(TimerEvent.TIMER, hideWarning);
hideWarningTimer.start();
}
// bring the label to front
setChildIndex(lblWarning, getChildren().length - 1);
lblWarning.text = text;
lblWarning.setStyle("color", color);
lblWarning.visible = true;
LogUtil.debug("Showing warning: " + text);
}
private function hideWarning(e:TimerEvent):void {
lblWarning.visible = false;
}
]]>
</mx:Script>
<mx:Fade id="dissolveOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
<mx:Fade id="dissolveIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>
<mx:UIComponent id="_videoHolder"/>
<mx:Text id="lblWarning" width="100%" textAlign="center" fontSize="14" fontWeight="bold"
y="{this.height - lblWarning.height - 30}"
visible="true" selectable="false" hideEffect="{dissolveOut}" showEffect="{dissolveIn}"/>
</mx:Canvas>

View File

@ -0,0 +1,254 @@
package org.bigbluebutton.main.views
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.StatusEvent;
import flash.events.TimerEvent;
import flash.events.ActivityEvent;
import flash.media.Camera;
import flash.media.Video
import flash.net.NetStream;
import flash.system.Security;
import flash.system.SecurityPanel;
import flash.utils.Timer;
import mx.containers.Canvas;
import mx.controls.Text;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.util.i18n.ResourceUtil;
public class VideoWithWarnings extends VideoWithWarningsBase {
private var hideWarningTimer:Timer = null;
private var _camera:Camera = null;
private var _activationTimer:Timer = null;
private var _waitingForActivation:Boolean = false;
private var _cameraAccessDenied:Boolean = false;
// TODO do something with this property
private var _chromePermissionDenied:Boolean = false;
private var _videoProfile:VideoProfile = null;
private var _showPreviewMsg:Boolean = false;
private var _video:Video = new Video();
private var _creationCompleted:Boolean = false;
private var _successCallback:Function = null;
private var _failCallback:Function = null;
public function VideoWithWarnings() {
super();
this.addEventListener(FlexEvent.CREATION_COMPLETE , creationCompleteHandler);
}
private function creationCompleteHandler(e:FlexEvent):void {
_video.smoothing = true;
_videoHolder.addChild(_video);
_creationCompleted = true;
}
public function cameraState():Boolean {
return _camera != null;
}
public function getCamera():Camera {
return _camera;
}
public function videoFilters(f:Array):void {
_video.filters = f;
}
private function hideWarning(e:TimerEvent):void {
_text.visible = false;
}
private function showMessageHelper(resourceName:String, autoHide:Boolean, styleName:String):void {
const text:String = ResourceUtil.getInstance().getString(resourceName);
if (hideWarningTimer != null) {
hideWarningTimer.stop()
hideWarningTimer = null;
}
if (autoHide) {
hideWarningTimer = new Timer(3000, 1);
hideWarningTimer.addEventListener(TimerEvent.TIMER, hideWarning);
hideWarningTimer.start();
}
_text.text = text;
// _text.text = "The quick brown fox jumps over the lazy dog";
_text.setStyle("styleName", styleName);
_text.visible = true;
trace("Showing warning: " + text);
}
private function showError(resourceName:String, autoHide:Boolean=false):void {
showMessageHelper(resourceName, autoHide, "videoMessageErrorLabelStyle");
}
private function showWarning(resourceName:String, autoHide:Boolean=false):void {
showMessageHelper(resourceName, autoHide, "videoMessageWarningLabelStyle");
}
public function set successCallback(f:Function):void {
_successCallback = f;
}
public function set failCallback(f:Function):void {
_failCallback = f;
}
private function onSuccessCallback():void {
if (_showPreviewMsg) {
showWarning('bbb.video.publish.hint.videoPreview');
} else {
_text.visible = false;
_text.text = " ";
}
if (_successCallback != null) {
_successCallback();
}
}
private function onFailCallback(resourceName:String):void {
showError(resourceName);
if (_failCallback != null) {
_failCallback();
}
}
public function updateCamera(camIndex:int, vp:VideoProfile, containerWidth:int, containerHeight:int, showPreviewMsg:Boolean=false):void {
disableCamera();
_videoProfile = vp;
_showPreviewMsg = showPreviewMsg;
_camera = Camera.getCamera(camIndex.toString());
if (camIndex == -1) {
onFailCallback('bbb.video.publish.hint.noCamera');
} else if (_camera == null) {
onFailCallback('bbb.video.publish.hint.cantOpenCamera');
} else {
_camera.addEventListener(ActivityEvent.ACTIVITY, onActivityEvent);
_camera.addEventListener(StatusEvent.STATUS, onStatusEvent);
if (_camera.muted) {
if (_cameraAccessDenied) {
Security.showSettings(SecurityPanel.PRIVACY)
} else {
showWarning('bbb.video.publish.hint.waitingApproval');
}
} else {
onCameraAccessAllowed();
}
displayVideoPreview();
}
this.width = containerWidth;
this.height = containerHeight;
invalidateDisplayList();
}
private function displayVideoPreview():void {
trace("Using this video profile:: " + _videoProfile.toString());
_camera.setMotionLevel(5, 1000);
_camera.setKeyFrameInterval(_videoProfile.keyFrameInterval);
_camera.setMode(_videoProfile.width, _videoProfile.height, _videoProfile.modeFps);
_camera.setQuality(_videoProfile.qualityBandwidth, _videoProfile.qualityPicture);
if (_camera.width != _videoProfile.width || _camera.height != _videoProfile.height)
trace("Resolution " + _videoProfile.width + "x" + _videoProfile.height + " is not supported, using " + _camera.width + "x" + _camera.height + " instead");
_video.attachCamera(_camera);
}
override protected function updateDisplayList(w:Number, h:Number):void {
super.updateDisplayList(w, h);
var videoWidth:int;
var videoHeight:int;
if (_creationCompleted && _videoProfile != null) {
var ar:Number = _videoProfile.width / _videoProfile.height;
if (w / h > ar) {
videoWidth = Math.ceil(h * ar);
videoHeight = h;
} else {
videoWidth = w;
videoHeight = Math.ceil(w / ar);
}
videoCanvas.width = _video.width = videoWidth;
videoCanvas.height = _video.height = videoHeight;
}
}
public function attachNetStream(ns:NetStream, vp:VideoProfile, containerWidth:int, containerHeight:int):void {
disableCamera();
_videoProfile = vp;
_video.attachNetStream(ns);
this.width = containerWidth;
this.height = containerHeight;
invalidateDisplayList();
}
public function disableCamera():void {
_video.clear();
_video.attachCamera(null);
_camera = null;
}
private function onActivityEvent(e:ActivityEvent):void {
if (_waitingForActivation && e.activating) {
_activationTimer.stop();
_waitingForActivation = false;
onSuccessCallback();
}
}
private function onStatusEvent(e:StatusEvent):void {
if (e.code == "Camera.Unmuted") {
onCameraAccessAllowed();
} else {
onCameraAccessDisallowed();
}
}
private function onCameraAccessAllowed():void {
// this is just to overwrite the message of waiting for approval
showWarning('bbb.video.publish.hint.openingCamera');
_cameraAccessDenied = false;
// set timer to ensure that the camera activates. If not, it might be in use by another application
_waitingForActivation = true;
if (_activationTimer != null) {
_activationTimer.stop();
}
_activationTimer = new Timer(10000, 1);
_activationTimer.addEventListener(TimerEvent.TIMER, activationTimeout);
_activationTimer.start();
}
private function onCameraAccessDisallowed():void {
onFailCallback('bbb.video.publish.hint.cameraDenied');
_cameraAccessDenied = true;
}
private function activationTimeout(e:TimerEvent):void {
onFailCallback('bbb.video.publish.hint.cameraIsBeingUsed');
}
public function set chromePermissionDenied(value:Boolean):void {
_chromePermissionDenied = value;
}
}
}

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:mate="http://mate.asfusion.com/" width="100%" height="100%"
horizontalAlign="center" >
<mx:Fade id="dissolveOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
<mx:Fade id="dissolveIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>
<mx:Canvas
id="videoCanvas"
width="100%"
height="100%"
horizontalScrollPolicy="off" verticalScrollPolicy="off" >
<mx:UIComponent
id="_videoHolder"
width="100%"
height="100%" />
<mx:VBox
width="100%"
height="100%"
verticalAlign="bottom"
visible="{_text.visible}"
includeInLayout="{_text.visible}"
hideEffect="{dissolveOut}" showEffect="{dissolveIn}" >
<mx:Box
width="100%"
styleName="videoMessageBackgroundStyle" >
<mx:Text
id="_text"
selectable="false"
textAlign="center"
visible="false"
width="100%" />
</mx:Box>
</mx:VBox>
</mx:Canvas>
</mx:VBox>

View File

@ -499,8 +499,8 @@ package org.bigbluebutton.modules.users.services
private function handleUserUnsharedWebcam(msg: Object):void {
trace(LOG + "*** handleUserUnsharedWebcam " + msg.msg + " **** \n");
var map:Object = JSON.parse(msg.msg);
UserManager.getInstance().getConference().unsharedWebcam(map.userId);
var map:Object = JSON.parse(msg.msg);
UserManager.getInstance().getConference().unsharedWebcam(map.userId, map.webcamStream);
}
public function participantStatusChange(userID:String, status:String, value:Object):void {
@ -531,11 +531,12 @@ package org.bigbluebutton.modules.users.services
UserManager.getInstance().getConference().addUser(user);
if (joinedUser.hasStream) {
UserManager.getInstance().getConference().sharedWebcam(user.userID, joinedUser.webcamStream);
} else {
UserManager.getInstance().getConference().unsharedWebcam(user.userID);
var streams:Array = joinedUser.webcamStream.split("|");
for each(var stream:String in streams) {
UserManager.getInstance().getConference().sharedWebcam(user.userID, stream);
}
}
UserManager.getInstance().getConference().presenterStatusChanged(user.userID, joinedUser.presenter);
UserManager.getInstance().getConference().raiseHand(user.userID, joinedUser.raiseHand);

View File

@ -123,7 +123,8 @@ package org.bigbluebutton.modules.users.services
},
function(status:String):void { // status - On error occurred
LogUtil.error(status);
}
},
streamName
);
}

View File

@ -211,7 +211,8 @@
}
if (data.hasStream) {
if (data.viewingStream || data.me) {
// if it's myself or if I'm watching all the streams from the given user, then don't activate the button
if (data.me || data.isViewingAllStreams()) {
webcamImg.visible = true;
webcamImg.includeInLayout = true;
webcamBtn.visible = false;

View File

@ -51,6 +51,8 @@ package org.bigbluebutton.modules.videoconf.business
private var nc:NetConnection;
private var ns:NetStream;
private var _url:String;
private var camerasPublishing:Object = new Object();
private var connected:Boolean = false;
private function parseOptions():void {
videoOptions = new VideoConfOptions();
@ -89,11 +91,13 @@ package org.bigbluebutton.modules.videoconf.business
private function onNetStatus(event:NetStatusEvent):void{
switch(event.info.code){
case "NetConnection.Connect.Success":
ns = new NetStream(nc);
connected = true;
//ns = new NetStream(nc);
onConnectedToVideoApp();
break;
default:
LogUtil.debug("[" + event.info.code + "] for [" + _url + "]");
connected = false;
break;
}
}
@ -106,6 +110,7 @@ package org.bigbluebutton.modules.videoconf.business
}
public function startPublishing(e:StartBroadcastEvent):void{
var ns:NetStream = new NetStream(nc);
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
ns.addEventListener( IOErrorEvent.IO_ERROR, onIOError );
ns.addEventListener( AsyncErrorEvent.ASYNC_ERROR, onAsyncError );
@ -113,47 +118,32 @@ package org.bigbluebutton.modules.videoconf.business
ns.attachCamera(e.camera);
// Uncomment if you want to build support for H264. But you need at least FP 11. (ralam july 23, 2011)
// if (Capabilities.version.search("11,0") != -1) {
if ((BBB.getFlashPlayerVersion() >= 11) && videoOptions.enableH264) {
if ((BBB.getFlashPlayerVersion() >= 11) && e.videoProfile.enableH264) {
// if (BBB.getFlashPlayerVersion() >= 11) {
LogUtil.info("Using H264 codec for video.");
var h264:H264VideoStreamSettings = new H264VideoStreamSettings();
var h264profile:String = H264Profile.MAIN;
if (videoOptions.h264Profile != "main") {
if (e.videoProfile.h264Profile != "main") {
h264profile = H264Profile.BASELINE;
}
var h264Level:String = H264Level.LEVEL_4_1;
if (videoOptions.h264Level == "1") {
h264Level = H264Level.LEVEL_1;
} else if (videoOptions.h264Level == "1.1") {
h264Level = H264Level.LEVEL_1_1;
} else if (videoOptions.h264Level == "1.2") {
h264Level = H264Level.LEVEL_1_2;
} else if (videoOptions.h264Level == "1.3") {
h264Level = H264Level.LEVEL_1_3;
} else if (videoOptions.h264Level == "1b") {
h264Level = H264Level.LEVEL_1B;
} else if (videoOptions.h264Level == "2") {
h264Level = H264Level.LEVEL_2;
} else if (videoOptions.h264Level == "2.1") {
h264Level = H264Level.LEVEL_2_1;
} else if (videoOptions.h264Level == "2.2") {
h264Level = H264Level.LEVEL_2_2;
} else if (videoOptions.h264Level == "3") {
h264Level = H264Level.LEVEL_3;
} else if (videoOptions.h264Level == "3.1") {
h264Level = H264Level.LEVEL_3_1;
} else if (videoOptions.h264Level == "3.2") {
h264Level = H264Level.LEVEL_3_2;
} else if (videoOptions.h264Level == "4") {
h264Level = H264Level.LEVEL_4;
} else if (videoOptions.h264Level == "4.1") {
h264Level = H264Level.LEVEL_4_1;
} else if (videoOptions.h264Level == "4.2") {
h264Level = H264Level.LEVEL_4_2;
} else if (videoOptions.h264Level == "5") {
h264Level = H264Level.LEVEL_5;
} else if (videoOptions.h264Level == "5.1") {
h264Level = H264Level.LEVEL_5_1;
switch (e.videoProfile.h264Level) {
case "1": h264Level = H264Level.LEVEL_1; break;
case "1.1": h264Level = H264Level.LEVEL_1_1; break;
case "1.2": h264Level = H264Level.LEVEL_1_2; break;
case "1.3": h264Level = H264Level.LEVEL_1_3; break;
case "1b": h264Level = H264Level.LEVEL_1B; break;
case "2": h264Level = H264Level.LEVEL_2; break;
case "2.1": h264Level = H264Level.LEVEL_2_1; break;
case "2.2": h264Level = H264Level.LEVEL_2_2; break;
case "3": h264Level = H264Level.LEVEL_3; break;
case "3.1": h264Level = H264Level.LEVEL_3_1; break;
case "3.2": h264Level = H264Level.LEVEL_3_2; break;
case "4": h264Level = H264Level.LEVEL_4; break;
case "4.1": h264Level = H264Level.LEVEL_4_1; break;
case "4.2": h264Level = H264Level.LEVEL_4_2; break;
case "5": h264Level = H264Level.LEVEL_5; break;
case "5.1": h264Level = H264Level.LEVEL_5_1; break;
}
LogUtil.info("Codec used: " + h264Level);
@ -163,22 +153,33 @@ package org.bigbluebutton.modules.videoconf.business
}
ns.publish(e.stream);
camerasPublishing[e.stream] = ns;
}
public function stopBroadcasting():void{
public function stopBroadcasting(stream:String):void{
trace("Closing netstream for webcam publishing");
if (ns != null) {
if (camerasPublishing[stream] != null) {
var ns:NetStream = camerasPublishing[stream];
ns.attachCamera(null);
ns.close();
ns = null;
ns = new NetStream(nc);
}
delete camerasPublishing[stream];
}
}
public function stopAllBroadcasting():void {
for each (var ns:NetStream in camerasPublishing)
{
ns.attachCamera(null);
ns.close();
ns = null;
}
camerasPublishing = new Object();
}
public function disconnect():void {
trace("VideoProxy:: disconnecting from Video application");
stopBroadcasting();
stopAllBroadcasting();
if (nc != null) nc.close();
}

View File

@ -1,319 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 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 3.0 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 com.asfusion.mate.events.Dispatcher;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.media.Video;
import flexlib.mdi.containers.MDIWindow;
import flexlib.mdi.events.MDIWindowEvent;
import mx.containers.Panel;
import mx.controls.Button;
import mx.core.UIComponent;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.DragWindowEvent;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.main.model.users.events.KickUserEvent;
import org.bigbluebutton.main.model.users.events.RoleChangeEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.events.UserTalkingEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.modules.videoconf.views.ControlButtons;
import org.bigbluebutton.util.i18n.ResourceUtil;
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 mousePositionOnDragStart:Point;
public var streamName:String;
private var windowType:String = "VideoWindowItf";
public var userID:String = null;
protected var _controlButtons:ControlButtons = new ControlButtons();
[Bindable] public var resolutions:Array;
protected var videoConfOptions:VideoConfOptions = new VideoConfOptions();
public function getWindowType():String {
return windowType;
}
protected function updateControlButtons():void {
_controlButtons.updateControlButtons();
}
protected function getVideoResolution(stream:String):Array {
var pattern:RegExp = new RegExp("(\\d+x\\d+)-([A-Za-z0-9]+)-\\d+", "");
if (pattern.test(stream)) {
LogUtil.debug("The stream name is well formatted [" + stream + "]");
var uid:String = UserManager.getInstance().getConference().getMyUserId();
LogUtil.debug("Stream resolution is [" + pattern.exec(stream)[1] + "]");
LogUtil.debug("Userid [" + pattern.exec(stream)[2] + "]");
userID = pattern.exec(stream)[2];
addControlButtons();
return pattern.exec(stream)[1].split("x");
} else {
LogUtil.error("The stream name doesn't follow the pattern <width>x<height>-<userId>-<timestamp>. However, the video resolution will be set to the lowest defined resolution in the config.xml: " + resolutions[0]);
return resolutions[0].split("x");
}
}
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 (_controlButtonsEnabled)
return MainCanvas.POPUP;
else
// the window is docked, so it should not be moved on reset layout
return MainCanvas.ABSOLUTE;
}
override public function close(event:MouseEvent = null):void{
trace("VideoWIndowItf close window event");
var e:CloseWindowEvent = new CloseWindowEvent();
e.window = this;
dispatchEvent(e);
super.close(event);
}
private var _controlButtonsEnabled: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;
private var img_mute_icon:Class = images.webcam_mute;
private var signOutIcon:Class = images.webcam_kickuser;
private var adminIcon:Class = images.webcam_make_presenter;
private var chatIcon:Class = images.webcam_private_chat;
protected function addControlButtons():void {
_controlButtons.sharerUserID = userID;
_controlButtons.visible = true;
this.addChild(_controlButtons);
}
protected function get controlButtons():ControlButtons {
if (_controlButtons == null) {
_controlButtons.visible = false;
}
return _controlButtons;
}
protected function createButtons():void {
updateButtonsPosition();
}
protected function updateButtonsPosition():void {
if (this.width < controlButtons.width) {
controlButtons.visible = false;
}
if (controlButtons.visible == false) {
controlButtons.y = controlButtons.x = 0;
} else {
controlButtons.y = this.height - PADDING_VERTICAL - controlButtons.height - controlButtons.padding;
controlButtons.x = this.width - PADDING_HORIZONTAL - controlButtons.width - controlButtons.padding;
}
}
protected function showButtons(event:MouseEvent = null):void {
if (_controlButtonsEnabled && controlButtons.visible == false && this.width > controlButtons.width) {
controlButtons.visible = true;
updateButtonsPosition();
}
}
protected function hideButtons(event:MouseEvent = null):void {
if (_controlButtonsEnabled && controlButtons.visible == true) {
controlButtons.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();
_controlButtonsEnabled = enabled;
}
protected function userMuted(muted:Boolean):void {
_controlButtons.userMuted(muted);
}
protected function simulateClick():void {
if (videoConfOptions.focusTalking) {
var talkingEvent:UserTalkingEvent = new UserTalkingEvent(UserTalkingEvent.TALKING);
dispatchEvent(talkingEvent);
}
}
}
}

View File

@ -0,0 +1,33 @@
/**
* 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;
public class CloseAllPublishWindowEvent extends Event
{
public static const CLOSE_ALL_PUBLISH_WINDOW:String = "CLOSE_ALL_PUBLISH_WINDOW";
public function CloseAllPublishWindowEvent(type:String = CLOSE_ALL_PUBLISH_WINDOW)
{
super(type, true, false);
}
}
}

View File

@ -25,6 +25,9 @@ package org.bigbluebutton.modules.videoconf.events
public static const SHARE_CAMERA_REQUEST:String = "ShareCameraRequestEvent";
public var publishInClient:Boolean = true;
public var defaultCamera:String = "0";
public var camerasArray:Object = null;
public function ShareCameraRequestEvent(type:String = SHARE_CAMERA_REQUEST)
{
@ -32,4 +35,4 @@ package org.bigbluebutton.modules.videoconf.events
}
}
}
}

View File

@ -21,12 +21,15 @@ package org.bigbluebutton.modules.videoconf.events
import flash.events.Event;
import flash.media.Camera;
import org.bigbluebutton.core.model.VideoProfile;
public class StartBroadcastEvent extends Event
{
public static const START_BROADCAST:String = "startBroadcastEvent";
public var stream:String;
public var camera:Camera;
public var videoProfile:VideoProfile;
public function StartBroadcastEvent(type:String = START_BROADCAST)
{

View File

@ -25,6 +25,7 @@ package org.bigbluebutton.modules.videoconf.events
public static const STOP_BROADCASTING:String = "STOP_BROADCASTING";
public var stream:String;
public var camId:int = -1;
public function StopBroadcastEvent(type:String = STOP_BROADCASTING)
{
@ -32,4 +33,4 @@ package org.bigbluebutton.modules.videoconf.events
}
}
}
}

View File

@ -0,0 +1,35 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 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 3.0 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;
public class StopShareCameraRequestEvent extends Event
{
public static const STOP_SHARE_CAMERA_REQUEST:String = "StopShareCameraRequestEvent";
public static const STOP_SHARE_ALL_CAMERA_REQUEST:String = "StopShareAllCameraRequestEvent";
public var camId:int;
public function StopShareCameraRequestEvent(type:String = STOP_SHARE_CAMERA_REQUEST)
{
super(type, true, false);
}
}
}

View File

@ -1,122 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import org.bigbluebutton.common.events.ToolbarButtonEvent;
import org.bigbluebutton.core.events.ConnectAppEvent;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.UserJoinedEvent;
import org.bigbluebutton.main.events.UserLeftEvent;
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.modules.users.events.ViewCameraEvent;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ConnectedEvent;
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.VideoModuleStartEvent;
import org.bigbluebutton.modules.videoconf.events.VideoModuleStopEvent;
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import org.bigbluebutton.core.events.ConnectAppEvent;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.UserJoinedEvent;
import org.bigbluebutton.main.events.UserLeftEvent;
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.modules.users.events.ViewCameraEvent;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ConnectedEvent;
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopShareCameraRequestEvent;
import org.bigbluebutton.modules.videoconf.events.VideoModuleStartEvent;
import org.bigbluebutton.modules.videoconf.events.VideoModuleStopEvent;
import org.bigbluebutton.modules.videoconf.events.WebRTCWebcamRequestEvent;
]]>
</mx:Script>
<EventHandlers type="{VideoModuleStartEvent.START}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="start" arguments="{event.uri}"/>
</EventHandlers>
<EventHandlers type="{VideoModuleStopEvent.STOP}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="stopModule"/>
</EventHandlers>
<EventHandlers type="{BBBEvent.CAMERA_SETTING}" >
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleCameraSetting" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{ConnectAppEvent.CONNECT_VIDEO_APP}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="connectToVideoApp" />
</EventHandlers>
<EventHandlers type="{ShareCameraRequestEvent.SHARE_CAMERA_REQUEST}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleShareCameraRequestEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{StartBroadcastEvent.START_BROADCAST}" >
<MethodInvoker generator="{VideoEventMapDelegate}" method="startPublishing" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{StopBroadcastEvent.STOP_BROADCASTING}" >
<MethodInvoker generator="{VideoEventMapDelegate}" method="stopPublishing" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{StreamStartedEvent.STREAM_STARTED}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.user]}" />
</EventHandlers>
<EventHandlers type="{ViewCameraEvent.VIEW_CAMERA_EVENT}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.viewedName]}" />
</EventHandlers>
<EventHandlers type="{UserJoinedEvent.JOINED}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleUserJoinedEvent" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{UserLeftEvent.LEFT}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleUserLeftEvent" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" >
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="switchToPresenter" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="switchToViewer" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{ConnectedEvent.VIDEO_CONNECTED}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="connectedToVideoApp" />
</EventHandlers>
<EventHandlers type="{ClosePublishWindowEvent.CLOSE_PUBLISH_WINDOW}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleClosePublishWindowEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{StoppedViewingWebcamEvent.STOPPED_VIEWING_WEBCAM}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleStoppedViewingWebcamEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{BBBEvent.CAM_SETTINGS_CLOSED}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleCamSettingsClosedEvent" arguments="{event}"/>
</EventHandlers>
<!-- ~~~~~~~~~~~~~~~~~~ INJECTORS ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
</EventMap>
]]>
</mx:Script>
<EventHandlers type="{VideoModuleStartEvent.START}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="start" arguments="{event.uri}"/>
<EventAnnouncer generator="{ConnectAppEvent}" type="{ConnectAppEvent.CONNECT_VIDEO_APP}" />
</EventHandlers>
<EventHandlers type="{VideoModuleStopEvent.STOP}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="stopModule"/>
</EventHandlers>
<EventHandlers type="{BBBEvent.CAMERA_SETTING}" >
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleCameraSetting" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{ConnectAppEvent.CONNECT_VIDEO_APP}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="connectToVideoApp" />
</EventHandlers>
<EventHandlers type="{ShareCameraRequestEvent.SHARE_CAMERA_REQUEST}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleShareCameraRequestEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{StopShareCameraRequestEvent.STOP_SHARE_CAMERA_REQUEST}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleStopShareCameraRequestEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{StopShareCameraRequestEvent.STOP_SHARE_ALL_CAMERA_REQUEST}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleStopAllShareCameraRequestEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{StartBroadcastEvent.START_BROADCAST}" >
<MethodInvoker generator="{VideoEventMapDelegate}" method="startPublishing" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{StopBroadcastEvent.STOP_BROADCASTING}" >
<MethodInvoker generator="{VideoEventMapDelegate}" method="stopPublishing" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{StreamStartedEvent.STREAM_STARTED}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.user]}" />
</EventHandlers>
<EventHandlers type="{ViewCameraEvent.VIEW_CAMERA_EVENT}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.viewedName]}" />
</EventHandlers>
<EventHandlers type="{UserJoinedEvent.JOINED}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleUserJoinedEvent" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{UserLeftEvent.LEFT}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleUserLeftEvent" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" >
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="switchToPresenter" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="switchToViewer" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{ConnectedEvent.VIDEO_CONNECTED}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="connectedToVideoApp" />
</EventHandlers>
<EventHandlers type="{ClosePublishWindowEvent.CLOSE_PUBLISH_WINDOW}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleClosePublishWindowEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{StoppedViewingWebcamEvent.STOPPED_VIEWING_WEBCAM}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleStoppedViewingWebcamEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{BBBEvent.CAM_SETTINGS_CLOSED}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleCamSettingsClosedEvent" arguments="{event}"/>
</EventHandlers>
<!-- ~~~~~~~~~~~~~~~~~~ INJECTORS ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
</EventMap>

View File

@ -1,500 +1,499 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 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 3.0 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.maps
{
import com.asfusion.mate.utils.debug.Debugger;
import com.asfusion.mate.utils.debug.DebuggerUtil;
import flash.events.IEventDispatcher;
import flash.external.ExternalInterface;
import flash.media.Camera;
import mx.collections.ArrayCollection;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.common.events.ToolbarButtonEvent;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.ConnectAppEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.core.vo.CameraSettingsVO;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.UserJoinedEvent;
import org.bigbluebutton.main.events.UserLeftEvent;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.main.model.users.events.BroadcastStartedEvent;
import org.bigbluebutton.main.model.users.events.BroadcastStoppedEvent;
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.modules.videoconf.business.VideoProxy;
import org.bigbluebutton.modules.videoconf.business.VideoWindowItf;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ConnectedEvent;
import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.WebRTCWebcamRequestEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.modules.videoconf.views.AvatarWindow;
import org.bigbluebutton.modules.videoconf.views.PublishWindow;
import org.bigbluebutton.modules.videoconf.views.ToolbarButton;
import org.bigbluebutton.modules.videoconf.views.VideoWindow;
import org.flexunit.runner.manipulation.filters.IncludeAllFilter;
public class VideoEventMapDelegate
{
static private var PERMISSION_DENIED_ERROR:String = "PermissionDeniedError";
private var options:VideoConfOptions = new VideoConfOptions();
private var uri:String;
private var webcamWindows:WindowManager = new WindowManager();
private var button:ToolbarButton = new ToolbarButton();
private var proxy:VideoProxy;
private var streamName:String;
private var _dispatcher:IEventDispatcher;
private var _ready:Boolean = false;
private var _isPublishing:Boolean = false;
private var _isPreviewWebcamOpen:Boolean = false;
private var _isWaitingActivation:Boolean = false;
private var _chromeWebcamPermissionDenied:Boolean = false;
public function VideoEventMapDelegate(dispatcher:IEventDispatcher)
{
_dispatcher = dispatcher;
}
private function get me():String {
return UsersUtil.getMyUsername();
}
public function start(uri:String):void {
trace("VideoEventMapDelegate:: [" + me + "] Video Module Started.");
this.uri = uri;
}
public function viewCamera(userID:String, stream:String, name:String, mock:Boolean = false):void {
trace("VideoEventMapDelegate:: [" + me + "] viewCamera. ready = [" + _ready + "]");
if (!_ready) return;
trace("VideoEventMapDelegate:: [" + me + "] Viewing [" + userID + " stream [" + stream + "]");
if (! UserManager.getInstance().getConference().amIThisUser(userID)) {
openViewWindowFor(userID);
}
}
public function handleUserLeftEvent(event:UserLeftEvent):void {
trace("VideoEventMapDelegate:: [" + me + "] handleUserLeftEvent. ready = [" + _ready + "]");
if (!_ready) return;
closeWindow(event.userID);
}
public function handleUserJoinedEvent(event:UserJoinedEvent):void {
trace("VideoEventMapDelegate:: [" + me + "] handleUserJoinedEvent. ready = [" + _ready + "]");
if (!_ready) return;
if (options.displayAvatar) {
openAvatarWindowFor(event.userID);
}
}
private function displayToolbarButton():void {
button.isPresenter = true;
if (options.presenterShareOnly) {
if (UsersUtil.amIPresenter()) {
button.isPresenter = true;
} else {
button.isPresenter = false;
}
}
}
private function addToolbarButton():void{
LogUtil.debug("****************** Adding toolbar button. presenter?=[" + UsersUtil.amIPresenter() + "]");
if (proxy.videoOptions.showButton) {
displayToolbarButton();
var event:ToolbarButtonEvent = new ToolbarButtonEvent(ToolbarButtonEvent.ADD);
event.button = button;
event.module="Webcam";
_dispatcher.dispatchEvent(event);
}
}
private function autoStart():void {
if (options.skipCamSettingsCheck) {
skipCameraSettingsCheck();
} else {
_dispatcher.dispatchEvent(new ShareCameraRequestEvent());
}
}
private function changeDefaultCamForMac():Camera {
for (var i:int = 0; i < Camera.names.length; i++){
if (Camera.names[i] == "USB Video Class Video") {
/** Set as default for Macs */
return Camera.getCamera("USB Video Class Video");
}
}
return null;
}
private function getDefaultResolution(resolutions:String):Array {
var res:Array = resolutions.split(",");
if (res.length > 0) {
var resStr:Array = (res[0] as String).split("x");
var resInts:Array = [Number(resStr[0]), Number(resStr[1])];
return resInts;
} else {
return [Number("320"), Number("240")];
}
}
private function skipCameraSettingsCheck():void {
var cam:Camera = changeDefaultCamForMac();
if (cam == null) {
cam = Camera.getCamera();
}
var videoOptions:VideoConfOptions = new VideoConfOptions();
var resolutions:Array = getDefaultResolution(videoOptions.resolutions);
var camWidth:Number = resolutions[0];
var camHeight:Number = resolutions[1];
trace("Skipping cam check. Using default resolution [" + camWidth + "x" + camHeight + "]");
cam.setMode(camWidth, camHeight, videoOptions.camModeFps);
cam.setMotionLevel(5, 1000);
cam.setKeyFrameInterval(videoOptions.camKeyFrameInterval);
cam.setQuality(videoOptions.camQualityBandwidth, videoOptions.camQualityPicture);
initCameraWithSettings(cam.index, cam.width, cam.height);
}
private function openWebcamWindows():void {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindows:: ready = [" + _ready + "]");
var uids:ArrayCollection = UsersUtil.getUserIDs();
for (var i:int = 0; i < uids.length; i++) {
var u:String = uids.getItemAt(i) as String;
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindows:: open window for = [" + u + "]");
openWebcamWindowFor(u);
}
}
private function openWebcamWindowFor(userID:String):void {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: open window for = [" + userID + "]");
if (! UsersUtil.isMe(userID) && UsersUtil.hasWebcamStream(userID)) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: Not ME and user = [" + userID + "] is publishing.");
if (webcamWindows.hasWindow(userID)) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: user = [" + userID + "] has a window open. Close it.");
closeWindow(userID);
}
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: View user's = [" + userID + "] webcam.");
openViewWindowFor(userID);
} else {
if (UsersUtil.isMe(userID) && options.autoStart) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: It's ME and AutoStart. Start publishing.");
autoStart();
} else {
if (options.displayAvatar) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: It's NOT ME and NOT AutoStart. Open Avatar for user = [" + userID + "]");
openAvatarWindowFor(userID);
} else {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: Is THERE another option for user = [" + userID + "]");
}
}
}
}
private function openAvatarWindowFor(userID:String):void {
if (! UsersUtil.hasUser(userID)) return;
var window:AvatarWindow = new AvatarWindow();
window.userID = userID;
window.title = UsersUtil.getUserName(userID);
trace("VideoEventMapDelegate:: [" + me + "] openAvatarWindowFor:: Closing window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
closeWindow(userID);
webcamWindows.addWindow(window);
trace("VideoEventMapDelegate:: [" + me + "] openAvatarWindowFor:: Opening AVATAR window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
openWindow(window);
dockWindow(window);
}
private function openPublishWindowFor(userID:String, camIndex:int, camWidth:int, camHeight:int):void {
var publishWindow:PublishWindow = new PublishWindow();
publishWindow.userID = userID;
publishWindow.title = UsersUtil.getUserName(userID);
publishWindow.camIndex = camIndex;
publishWindow.setResolution(camWidth, camHeight);
publishWindow.videoOptions = options;
publishWindow.quality = options.videoQuality;
publishWindow.chromePermissionDenied = _chromeWebcamPermissionDenied;
publishWindow.resolutions = options.resolutions.split(",");
trace("VideoEventMapDelegate:: [" + me + "] openPublishWindowFor:: Closing window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
closeWindow(userID);
webcamWindows.addWindow(publishWindow);
trace("VideoEventMapDelegate:: [" + me + "] openPublishWindowFor:: Opening PUBLISH window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
openWindow(publishWindow);
dockWindow(publishWindow);
}
private function closeWindow(userID:String):void {
if (! webcamWindows.hasWindow(userID)) {
trace("VideoEventMapDelegate:: [" + me + "] closeWindow:: No window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
return;
}
var win:VideoWindowItf = webcamWindows.removeWindow(userID);
if (win != null) {
trace("VideoEventMapDelegate:: [" + me + "] closeWindow:: Closing [" + win.getWindowType() + "] for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
win.close();
var cwe:CloseWindowEvent = new CloseWindowEvent();
cwe.window = win;
_dispatcher.dispatchEvent(cwe);
} else {
trace("VideoEventMapDelegate:: [" + me + "] closeWindow:: Not Closing. No window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
}
}
private function openViewWindowFor(userID:String):void {
trace("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: Opening VIEW window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
var window:VideoWindow = new VideoWindow();
window.userID = userID;
window.videoOptions = options;
window.resolutions = options.resolutions.split(",");
window.title = UsersUtil.getUserName(userID);
closeWindow(userID);
var bbbUser:BBBUser = UsersUtil.getUser(userID);
window.startVideo(proxy.connection, bbbUser.streamName);
webcamWindows.addWindow(window);
openWindow(window);
dockWindow(window);
}
private function openWindow(window:VideoWindowItf):void {
var windowEvent:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
windowEvent.window = window;
_dispatcher.dispatchEvent(windowEvent);
}
private function dockWindow(window:VideoWindowItf):void {
// this event will dock the window, if it's enabled
var openVideoEvent:OpenVideoWindowEvent = new OpenVideoWindowEvent();
openVideoEvent.window = window;
_dispatcher.dispatchEvent(openVideoEvent);
}
public function connectToVideoApp():void {
proxy = new VideoProxy(uri);
proxy.connect();
}
public function startPublishing(e:StartBroadcastEvent):void{
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] startPublishing:: Publishing stream to: " + proxy.connection.uri + "/" + e.stream);
streamName = e.stream;
proxy.startPublishing(e);
_isWaitingActivation = false;
_isPublishing = true;
UsersUtil.setIAmPublishing(true);
var broadcastEvent:BroadcastStartedEvent = new BroadcastStartedEvent();
broadcastEvent.stream = e.stream;
broadcastEvent.userid = UsersUtil.getMyUserID();
broadcastEvent.isPresenter = UsersUtil.amIPresenter();
broadcastEvent.camSettings = UsersUtil.amIPublishing();
_dispatcher.dispatchEvent(broadcastEvent);
if (proxy.videoOptions.showButton) {
button.publishingStatus(button.START_PUBLISHING);
}
}
public function stopPublishing(e:StopBroadcastEvent):void{
trace("VideoEventMapDelegate:: [" + me + "] Stop publishing. ready = [" + _ready + "]");
stopBroadcasting();
}
private function stopBroadcasting():void {
trace("Stopping broadcast of webcam");
proxy.stopBroadcasting();
_isPublishing = false;
UsersUtil.setIAmPublishing(false);
var broadcastEvent:BroadcastStoppedEvent = new BroadcastStoppedEvent();
broadcastEvent.stream = streamName;
broadcastEvent.userid = UsersUtil.getMyUserID();
broadcastEvent.avatarURL = UsersUtil.getAvatarURL();
_dispatcher.dispatchEvent(broadcastEvent);
if (proxy.videoOptions.showButton) {
//Make toolbar button enabled again
button.publishingStatus(button.STOP_PUBLISHING);
}
closeWindow(UsersUtil.getMyUserID());
if (options.displayAvatar) {
trace("VideoEventMapDelegate:: [" + me + "] Opening avatar");
openAvatarWindowFor(UsersUtil.getMyUserID());
}
}
public function handleClosePublishWindowEvent(event:ClosePublishWindowEvent):void {
trace("Closing publish window");
if (_isPublishing || _chromeWebcamPermissionDenied) {
stopBroadcasting();
}
trace("Resetting flags for publish window.");
// Reset flags to determine if we are publishing or previewing webcam.
_isPublishing = false;
_isWaitingActivation = false;
}
public function handleShareCameraRequestEvent(event:ShareCameraRequestEvent):void {
if (options.skipCamSettingsCheck) {
skipCameraSettingsCheck();
} else {
trace("Webcam: "+_isPublishing + " " + _isPreviewWebcamOpen + " " + _isWaitingActivation);
if (!_isPublishing && !_isPreviewWebcamOpen && !_isWaitingActivation) {
openWebcamPreview(event.publishInClient);
}
}
}
public function handleCamSettingsClosedEvent(event:BBBEvent):void{
_isPreviewWebcamOpen = false;
}
private function openWebcamPreview(publishInClient:Boolean):void {
var openEvent:BBBEvent = new BBBEvent(BBBEvent.OPEN_WEBCAM_PREVIEW);
openEvent.payload.publishInClient = publishInClient;
openEvent.payload.resolutions = options.resolutions;
openEvent.payload.chromePermissionDenied = _chromeWebcamPermissionDenied;
_isPreviewWebcamOpen = true;
_dispatcher.dispatchEvent(openEvent);
}
public function stopModule():void {
trace("VideoEventMapDelegate:: stopping video module");
closeAllWindows();
proxy.disconnect();
}
public function closeAllWindows():void{
trace("VideoEventMapDelegate:: closing all windows");
if (_isPublishing) {
stopBroadcasting();
}
_dispatcher.dispatchEvent(new CloseAllWindowsEvent());
}
public function switchToPresenter(event:MadePresenterEvent):void{
trace("VideoEventMapDelegate:: [" + me + "] Got Switch to presenter event. ready = [" + _ready + "]");
if (options.showButton) {
displayToolbarButton();
}
}
public function switchToViewer(event:MadePresenterEvent):void{
trace("VideoEventMapDelegate:: [" + me + "] Got Switch to viewer event. ready = [" + _ready + "]");
if (options.showButton){
LogUtil.debug("****************** Switching to viewer. Show video button?=[" + UsersUtil.amIPresenter() + "]");
displayToolbarButton();
if (_isPublishing && options.presenterShareOnly) {
stopBroadcasting();
}
}
}
public function connectedToVideoApp():void{
trace("VideoEventMapDelegate:: [" + me + "] Connected to video application.");
_ready = true;
addToolbarButton();
openWebcamWindows();
}
public function handleCameraSetting(event:BBBEvent):void {
var cameraIndex:int = event.payload.cameraIndex;
var camWidth:int = event.payload.cameraWidth;
var camHeight:int = event.payload.cameraHeight;
trace("VideoEventMapDelegate::handleCameraSettings [" + cameraIndex + "," + camWidth + "," + camHeight + "]");
initCameraWithSettings(cameraIndex, camWidth, camHeight);
}
private function initCameraWithSettings(camIndex:int, camWidth:int, camHeight:int):void {
var camSettings:CameraSettingsVO = new CameraSettingsVO();
camSettings.camIndex = camIndex;
camSettings.camWidth = camWidth;
camSettings.camHeight = camHeight;
UsersUtil.setCameraSettings(camSettings);
_isWaitingActivation = true;
openPublishWindowFor(UsersUtil.getMyUserID(), camIndex, camWidth, camHeight);
}
public function handleStoppedViewingWebcamEvent(event:StoppedViewingWebcamEvent):void {
trace("VideoEventMapDelegate::handleStoppedViewingWebcamEvent [" + me + "] received StoppedViewingWebcamEvent for user [" + event.webcamUserID + "]");
closeWindow(event.webcamUserID);
if (options.displayAvatar && UsersUtil.hasUser(event.webcamUserID) && ! UsersUtil.isUserLeaving(event.webcamUserID)) {
trace("VideoEventMapDelegate::handleStoppedViewingWebcamEvent [" + me + "] Opening avatar for user [" + event.webcamUserID + "]");
openAvatarWindowFor(event.webcamUserID);
}
}
}
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 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 3.0 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.maps
{
import com.asfusion.mate.utils.debug.Debugger;
import com.asfusion.mate.utils.debug.DebuggerUtil;
import flash.events.IEventDispatcher;
import flash.external.ExternalInterface;
import flash.media.Camera;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.common.events.ToolbarButtonEvent;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.ConnectAppEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.core.vo.CameraSettingsVO;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.UserJoinedEvent;
import org.bigbluebutton.main.events.UserLeftEvent;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.main.model.users.events.BroadcastStartedEvent;
import org.bigbluebutton.main.model.users.events.BroadcastStoppedEvent;
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.modules.videoconf.business.VideoProxy;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ConnectedEvent;
import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopShareCameraRequestEvent;
import org.bigbluebutton.modules.videoconf.events.WebRTCWebcamRequestEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.modules.videoconf.views.AvatarWindow;
import org.bigbluebutton.modules.videoconf.views.GraphicsWrapper;
import org.bigbluebutton.modules.videoconf.views.ToolbarPopupButton;
import org.bigbluebutton.modules.videoconf.views.UserAvatar;
import org.bigbluebutton.modules.videoconf.views.UserGraphic;
import org.bigbluebutton.modules.videoconf.views.UserGraphicHolder;
import org.bigbluebutton.modules.videoconf.views.UserVideo;
import org.bigbluebutton.modules.videoconf.views.VideoDock;
import org.flexunit.runner.manipulation.filters.IncludeAllFilter;
public class VideoEventMapDelegate
{
static private var PERMISSION_DENIED_ERROR:String = "PermissionDeniedError";
private var options:VideoConfOptions = new VideoConfOptions();
private var uri:String;
private var button:ToolbarPopupButton = new ToolbarPopupButton();
private var proxy:VideoProxy;
private var _dispatcher:IEventDispatcher;
private var _ready:Boolean = false;
private var _isPublishing:Boolean = false;
private var _isPreviewWebcamOpen:Boolean = false;
private var _isWaitingActivation:Boolean = false;
private var _chromeWebcamPermissionDenied:Boolean = false;
private var _videoDock:VideoDock;
private var _graphics:GraphicsWrapper = new GraphicsWrapper();
private var streamList:ArrayList = new ArrayList();
private var numberOfWindows:Object = new Object();
public function VideoEventMapDelegate(dispatcher:IEventDispatcher)
{
_dispatcher = dispatcher;
}
private function get me():String {
return UsersUtil.getMyUsername();
}
public function start(uri:String):void {
trace("VideoEventMapDelegate:: [" + me + "] Video Module Started.");
this.uri = uri;
_videoDock = new VideoDock();
var windowEvent:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
windowEvent.window = _videoDock;
_dispatcher.dispatchEvent(windowEvent);
_videoDock.addChild(_graphics);
}
public function viewCamera(userID:String, stream:String, name:String, mock:Boolean = false):void {
trace("VideoEventMapDelegate:: [" + me + "] viewCamera. ready = [" + _ready + "]");
if (!_ready) return;
trace("VideoEventMapDelegate:: [" + me + "] Viewing [" + userID + " stream [" + stream + "]");
if (! UserManager.getInstance().getConference().amIThisUser(userID)) {
openViewWindowFor(userID);
}
}
public function handleUserLeftEvent(event:UserLeftEvent):void {
trace("VideoEventMapDelegate:: [" + me + "] handleUserLeftEvent. ready = [" + _ready + "]");
if (!_ready) return;
closeWindow(event.userID);
}
public function handleUserJoinedEvent(event:UserJoinedEvent):void {
trace("VideoEventMapDelegate:: [" + me + "] handleUserJoinedEvent. ready = [" + _ready + "]");
if (!_ready) return;
if (options.displayAvatar) {
openAvatarWindowFor(event.userID);
}
}
private function displayToolbarButton():void {
button.isPresenter = true;
if (options.presenterShareOnly) {
if (UsersUtil.amIPresenter()) {
button.isPresenter = true;
} else {
button.isPresenter = false;
}
}
}
private function addToolbarButton():void{
LogUtil.debug("****************** Adding toolbar button. presenter?=[" + UsersUtil.amIPresenter() + "]");
if (proxy.videoOptions.showButton) {
displayToolbarButton();
var event:ToolbarButtonEvent = new ToolbarButtonEvent(ToolbarButtonEvent.ADD);
event.button = button;
event.module="Webcam";
_dispatcher.dispatchEvent(event);
}
}
private function autoStart():void {
if (options.skipCamSettingsCheck) {
skipCameraSettingsCheck();
} else {
var dp:Object = [];
for(var i:int = 0; i < Camera.names.length; i++) {
dp.push({label: Camera.names[i], status: button.OFF_STATE});
}
button.enabled = false;
var shareCameraRequestEvent:ShareCameraRequestEvent = new ShareCameraRequestEvent();
shareCameraRequestEvent.camerasArray = dp;
_dispatcher.dispatchEvent(shareCameraRequestEvent);
}
}
private function changeDefaultCamForMac():Camera {
for (var i:int = 0; i < Camera.names.length; i++){
if (Camera.names[i] == "USB Video Class Video") {
/** Set as default for Macs */
return Camera.getCamera("USB Video Class Video");
}
}
return null;
}
private function skipCameraSettingsCheck(camIndex:int = -1):void {
if (camIndex == -1) {
var cam:Camera = changeDefaultCamForMac();
if (cam == null) {
cam = Camera.getCamera();
}
camIndex = cam.index;
}
var videoProfile:VideoProfile = BBB.defaultVideoProfile;
initCameraWithSettings(camIndex, videoProfile);
}
private function openWebcamWindows():void {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindows:: ready = [" + _ready + "]");
var uids:ArrayCollection = UsersUtil.getUserIDs();
for (var i:int = 0; i < uids.length; i++) {
var u:String = uids.getItemAt(i) as String;
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindows:: open window for = [" + u + "]");
openWebcamWindowFor(u);
}
}
private function openWebcamWindowFor(userID:String):void {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: open window for = [" + userID + "]");
if (! UsersUtil.isMe(userID) && UsersUtil.hasWebcamStream(userID)) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: Not ME and user = [" + userID + "] is publishing.");
if (hasWindow(userID)) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: user = [" + userID + "] has a window open. Close it.");
closeWindow(userID);
}
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: View user's = [" + userID + "] webcam.");
openViewWindowFor(userID);
} else {
if (UsersUtil.isMe(userID) && options.autoStart) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: It's ME and AutoStart. Start publishing.");
autoStart();
} else {
if (options.displayAvatar) {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: It's NOT ME and NOT AutoStart. Open Avatar for user = [" + userID + "]");
openAvatarWindowFor(userID);
} else {
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: Is THERE another option for user = [" + userID + "]");
}
}
}
}
private function openAvatarWindowFor(userID:String):void {
if (! UsersUtil.hasUser(userID)) return;
closeAllAvatarWindows(userID);
_graphics.addAvatarFor(userID);
}
private function closeAllAvatarWindows(userID:String):void {
_graphics.removeAvatarFor(userID);
}
private function openPublishWindowFor(userID:String, camIndex:int, videoProfile:VideoProfile):void {
closeAllAvatarWindows(userID);
_graphics.addCameraFor(userID, camIndex, videoProfile);
}
private function hasWindow(userID:String):Boolean {
return _graphics.hasGraphicsFor(userID);
}
private function closeWindow(userID:String):void {
_graphics.removeGraphicsFor(userID);
}
private function closePublishWindowWithStream(userID:String, stream:String):int {
return _graphics.removeVideoByStreamName(userID, stream);
}
private function openViewWindowFor(userID:String):void {
trace("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: Opening VIEW window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
var bbbUser:BBBUser = UsersUtil.getUser(userID);
if (bbbUser.hasStream) {
closeAllAvatarWindows(userID);
}
_graphics.addVideoFor(userID, proxy.connection);
}
public function connectToVideoApp():void {
proxy = new VideoProxy(uri);
proxy.connect();
}
public function startPublishing(e:StartBroadcastEvent):void{
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] startPublishing:: Publishing stream to: " + proxy.connection.uri + "/" + e.stream);
proxy.startPublishing(e);
_isWaitingActivation = false;
_isPublishing = true;
UsersUtil.setIAmPublishing(true);
var broadcastEvent:BroadcastStartedEvent = new BroadcastStartedEvent();
streamList.addItem(e.stream);
broadcastEvent.stream = e.stream;
broadcastEvent.userid = UsersUtil.getMyUserID();
broadcastEvent.isPresenter = UsersUtil.amIPresenter();
broadcastEvent.camSettings = UsersUtil.amIPublishing();
_dispatcher.dispatchEvent(broadcastEvent);
if (proxy.videoOptions.showButton) {
button.publishingStatus(button.START_PUBLISHING);
}
}
public function stopPublishing(e:StopBroadcastEvent):void{
trace("VideoEventMapDelegate:: [" + me + "] Stop publishing. ready = [" + _ready + "]");
if(streamList.length <= 1) {
setStopLastBroadcasting();
} else {
UsersUtil.setIAmPublishing(true);
}
streamList.removeItem(e.stream);
stopBroadcasting(e.stream);
button.setCamAsInactive(e.camId);
}
private function stopAllBroadcasting():void {
trace("[VideoEventMapDelegate:stopAllBroadcasting]");
setStopLastBroadcasting();
streamList = new ArrayList();
proxy.stopAllBroadcasting();
var userID:String = UsersUtil.getMyUserID();
_graphics.removeGraphicsFor(userID);
var broadcastEvent:BroadcastStoppedEvent = new BroadcastStoppedEvent();
broadcastEvent.stream = "";
broadcastEvent.userid = UsersUtil.getMyUserID();
broadcastEvent.avatarURL = UsersUtil.getAvatarURL();
_dispatcher.dispatchEvent(broadcastEvent);
if (proxy.videoOptions.showButton) {
//Make toolbar button enabled again
button.setAllCamAsInactive();
}
if (options.displayAvatar) {
trace("VideoEventMapDelegate:: [" + me + "] Opening avatar");
openAvatarWindowFor(UsersUtil.getMyUserID());
}
}
private function setStopLastBroadcasting():void {
trace("[VideoEventMapDelegate:setStopLastBroadcasting]");
_isPublishing = false;
UsersUtil.setIAmPublishing(false);
}
private function stopBroadcasting(stream:String):void {
trace("Stopping broadcast of stream [" + stream + "]");
proxy.stopBroadcasting(stream);
var broadcastEvent:BroadcastStoppedEvent = new BroadcastStoppedEvent();
broadcastEvent.stream = stream;
broadcastEvent.userid = UsersUtil.getMyUserID();
broadcastEvent.avatarURL = UsersUtil.getAvatarURL();
_dispatcher.dispatchEvent(broadcastEvent);
var camId:int = closePublishWindowWithStream(UsersUtil.getMyUserID(), stream);
if (proxy.videoOptions.showButton) {
//Make toolbar button enabled again
button.publishingStatus(button.STOP_PUBLISHING, camId);
}
if (streamList.length == 0 && options.displayAvatar) {
trace("VideoEventMapDelegate:: [" + me + "] Opening avatar");
openAvatarWindowFor(UsersUtil.getMyUserID());
}
}
public function handleClosePublishWindowEvent(event:ClosePublishWindowEvent):void {
trace("Closing publish window");
if (_isPublishing || _chromeWebcamPermissionDenied) {
stopAllBroadcasting();
}
}
public function handleShareCameraRequestEvent(event:ShareCameraRequestEvent):void {
trace("[VideoEventMapDelegate:handleShareCameraRequestEvent]");
if (options.skipCamSettingsCheck) {
skipCameraSettingsCheck(int(event.defaultCamera));
} else {
openWebcamPreview(event.publishInClient, event.defaultCamera, event.camerasArray);
}
}
public function handleStopAllShareCameraRequestEvent(event:StopShareCameraRequestEvent):void {
trace("[VideoEventMapDelegate:handleStopAllShareCameraRequestEvent]");
stopAllBroadcasting();
}
public function handleStopShareCameraRequestEvent(event:StopShareCameraRequestEvent):void {
trace("[VideoEventMapDelegate:handleStopShareCameraRequestEvent]");
var userID:String = UsersUtil.getMyUserID();
var camIndex:int = event.camId;
_graphics.removeVideoByCamIndex(userID, camIndex);
}
public function handleCamSettingsClosedEvent(event:BBBEvent):void{
_isPreviewWebcamOpen = false;
}
private function openWebcamPreview(publishInClient:Boolean, defaultCamera:String, camerasArray:Object):void {
var openEvent:BBBEvent = new BBBEvent(BBBEvent.OPEN_WEBCAM_PREVIEW);
openEvent.payload.publishInClient = publishInClient;
openEvent.payload.defaultCamera = defaultCamera;
openEvent.payload.camerasArray = camerasArray;
openEvent.payload.chromePermissionDenied = _chromeWebcamPermissionDenied;
_isPreviewWebcamOpen = true;
_dispatcher.dispatchEvent(openEvent);
}
public function stopModule():void {
trace("VideoEventMapDelegate:: stopping video module");
closeAllWindows();
proxy.disconnect();
}
public function closeAllWindows():void{
trace("VideoEventMapDelegate:: closing all windows");
if (_isPublishing) {
stopAllBroadcasting();
}
_graphics.shutdown();
}
public function switchToPresenter(event:MadePresenterEvent):void{
trace("VideoEventMapDelegate:: [" + me + "] Got Switch to presenter event. ready = [" + _ready + "]");
if (options.showButton) {
displayToolbarButton();
}
}
public function switchToViewer(event:MadePresenterEvent):void{
trace("VideoEventMapDelegate:: [" + me + "] Got Switch to viewer event. ready = [" + _ready + "]");
if (options.showButton){
LogUtil.debug("****************** Switching to viewer. Show video button?=[" + UsersUtil.amIPresenter() + "]");
displayToolbarButton();
if (_isPublishing && options.presenterShareOnly) {
stopAllBroadcasting();
}
}
}
public function connectedToVideoApp():void{
trace("VideoEventMapDelegate:: [" + me + "] Connected to video application.");
_ready = true;
addToolbarButton();
openWebcamWindows();
}
public function handleCameraSetting(event:BBBEvent):void {
var cameraIndex:int = event.payload.cameraIndex;
var videoProfile:VideoProfile = event.payload.videoProfile;
trace("VideoEventMapDelegate::handleCameraSettings [" + cameraIndex + "," + videoProfile.id + "]");
initCameraWithSettings(cameraIndex, videoProfile);
}
private function initCameraWithSettings(camIndex:int, videoProfile:VideoProfile):void {
var camSettings:CameraSettingsVO = new CameraSettingsVO();
camSettings.camIndex = camIndex;
camSettings.videoProfile = videoProfile;
UsersUtil.setCameraSettings(camSettings);
_isWaitingActivation = true;
button.setCamAsActive(camIndex);
openPublishWindowFor(UsersUtil.getMyUserID(), camIndex, videoProfile);
}
private function closeViewWindowWithStream(userID:String, stream:String):void {
_graphics.removeVideoByStreamName(userID, stream);
}
public function handleStoppedViewingWebcamEvent(event:StoppedViewingWebcamEvent):void {
trace("VideoEventMapDelegate::handleStoppedViewingWebcamEvent [" + me + "] received StoppedViewingWebcamEvent for user [" + event.webcamUserID + "]");
closeViewWindowWithStream(event.webcamUserID, event.streamName);
if (options.displayAvatar && UsersUtil.hasUser(event.webcamUserID) && ! UsersUtil.isUserLeaving(event.webcamUserID)) {
trace("VideoEventMapDelegate::handleStoppedViewingWebcamEvent [" + me + "] Opening avatar for user [" + event.webcamUserID + "]");
openAvatarWindowFor(event.webcamUserID);
}
}
}
}

View File

@ -25,6 +25,7 @@ package org.bigbluebutton.modules.videoconf.maps
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.modules.videoconf.business.VideoWindowItf;
import org.bigbluebutton.modules.videoconf.views.AvatarWindow;
import mx.collections.ArrayList;
public class WindowManager
{
@ -50,6 +51,17 @@ package org.bigbluebutton.modules.videoconf.maps
return null;
}
public function removeWin(window:VideoWindowItf):VideoWindowItf {
for (var i:int = 0; i < webcamWindows.length; i++) {
var win:VideoWindowItf = webcamWindows.getItemAt(i) as VideoWindowItf;
if (win == window) {
return webcamWindows.removeItemAt(i) as VideoWindowItf;
}
}
return null;
}
public function hasWindow(userID:String):Boolean {
trace("[" + me + "] hasWindow:: user [" + userID + "] numWindows = [" + webcamWindows.length + "]");
@ -73,5 +85,16 @@ package org.bigbluebutton.modules.videoconf.maps
return null;
}
public function getAllWindow(userID:String):ArrayList {
var windowsList:ArrayList = new ArrayList();
for (var i:int = 0; i < webcamWindows.length; i++) {
var win:VideoWindowItf = webcamWindows.getItemAt(i) as VideoWindowItf;
trace("[" + me + "] getWindow:: [" + win.userID + " == " + userID + "] equal = [" + (win.userID == userID) + "]");
if (win.userID == userID) windowsList.addItem(win);
}
return windowsList;
}
}
}
}

View File

@ -24,12 +24,6 @@ package org.bigbluebutton.modules.videoconf.model
{
public var uri:String = "rtmp://localhost/video";
[Bindable]
public var videoQuality:Number = 100;
[Bindable]
public var resolutions:String = "320x240,640x480,1280x720";
[Bindable]
public var autoStart:Boolean = false;
@ -48,15 +42,6 @@ package org.bigbluebutton.modules.videoconf.model
[Bindable]
public var viewerWindowLocation:String = "middle";
[Bindable]
public var camKeyFrameInterval:Number = 5;
[Bindable]
public var camModeFps:Number = 15;
[Bindable]
public var camQualityBandwidth:Number = 0;
[Bindable]
public var smoothVideo:Boolean = false;
@ -72,18 +57,6 @@ package org.bigbluebutton.modules.videoconf.model
[Bindable]
public var filterDivisor:Number = 4;
[Bindable]
public var enableH264:Boolean = false;
[Bindable]
public var h264Level:String = "2.1";
[Bindable]
public var h264Profile:String = "main";
[Bindable]
public var camQualityPicture:Number = 50;
[Bindable] public var baseTabIndex:int;
[Bindable]
@ -106,6 +79,8 @@ package org.bigbluebutton.modules.videoconf.model
[Bindable]
public var glowBlurSize:Number = 30.0;
[Bindable]
public var priorityRatio:Number = 2/3;
public function VideoConfOptions() {
parseOptions();
@ -117,12 +92,6 @@ package org.bigbluebutton.modules.videoconf.model
if (vxml.@uri != undefined) {
uri = vxml.@uri.toString();
}
if (vxml.@videoQuality != undefined) {
videoQuality = Number(vxml.@videoQuality.toString());
}
if (vxml.@resolutions != undefined) {
resolutions = vxml.@resolutions.toString();
}
if (vxml.@showCloseButton != undefined) {
showCloseButton = (vxml.@showCloseButton.toString().toUpperCase() == "TRUE") ? true : false;
}
@ -153,18 +122,6 @@ package org.bigbluebutton.modules.videoconf.model
if (vxml.@viewerWindowLocation != undefined) {
viewerWindowLocation = vxml.@viewerWindowLocation.toString().toUpperCase();
}
if (vxml.@camKeyFrameInterval != undefined) {
camKeyFrameInterval = Number(vxml.@camKeyFrameInterval.toString());
}
if (vxml.@camModeFps != undefined) {
camModeFps = Number(vxml.@camModeFps.toString());
}
if (vxml.@camQualityBandwidth != undefined) {
camQualityBandwidth = Number(vxml.@camQualityBandwidth.toString());
}
if (vxml.@camQualityPicture != undefined) {
camQualityPicture = Number(vxml.@camQualityPicture.toString());
}
if (vxml.@smoothVideo != undefined) {
smoothVideo = (vxml.@smoothVideo.toString().toUpperCase() == "TRUE") ? true : false;
}
@ -184,16 +141,7 @@ package org.bigbluebutton.modules.videoconf.model
if (vxml.@filterDivisor != undefined) {
filterDivisor = Number(vxml.@filterDivisor.toString());
}
if (vxml.@enableH264 != undefined) {
enableH264 = (vxml.@enableH264.toString().toUpperCase() == "TRUE") ? true : false;
}
if (vxml.@h264Level != undefined) {
h264Level = vxml.@h264Level.toString();
}
if (vxml.@h264Profile != undefined) {
h264Profile = vxml.@h264Profile.toString();
}
if (vxml.@baseTabIndex != undefined) {
baseTabIndex = vxml.@baseTabIndex;
}
@ -216,6 +164,9 @@ package org.bigbluebutton.modules.videoconf.model
if (vxml.@glowBlurSize != undefined) {
glowBlurSize = Number(vxml.@glowBlurSize.toString());
}
if (vxml.@priorityRatio != undefined) {
priorityRatio = Number(vxml.@priorityRatio.toString());
}
}
}
}

View File

@ -36,37 +36,39 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mate:Listener type="{BBBEvent.USER_VOICE_MUTED}" method="handleUserVoiceMutedEvent" />
<mate:Listener type="{EventConstants.USER_TALKING}" method="handleUserTalkingEvent" />
<mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" />
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="handleMadePresenterEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_JOINED}" method="handleNewRoleEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_LEFT}" method="handleNewRoleEvent" />
<mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" />
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="handleMadePresenterEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_JOINED}" method="handleNewRoleEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_LEFT}" method="handleNewRoleEvent" />
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
<mx:Script>
<![CDATA[
import flexlib.mdi.events.MDIWindowEvent;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
import mx.events.ResizeEvent;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.Role;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.business.TalkingButtonOverlay;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
import mx.core.UIComponent;
import mx.events.ResizeEvent;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.Role;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.events.SwitchedLayoutEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.business.TalkingButtonOverlay;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
[Bindable] private var defaultWidth:Number = 320;
[Bindable] private var defaultHeight:Number = 240;

View File

@ -1,239 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:mate="http://mate.asfusion.com/"
creationComplete="onCreationComplete()">
<mate:Listener type="{LockControlEvent.CHANGED_LOCK_SETTINGS}" method="lockSettingsChanged" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.events.LockControlEvent;
import org.bigbluebutton.core.events.VoiceConfEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.main.model.users.Conference;
import org.bigbluebutton.main.model.users.events.KickUserEvent;
import org.bigbluebutton.main.model.users.events.RoleChangeEvent;
import org.bigbluebutton.modules.chat.model.ChatOptions;
import org.bigbluebutton.modules.users.model.UsersOptions;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
public var sharerUserID:String;
[Bindable]
private var BUTTONS_SIZE:int = 31;
private var BUTTONS_PADDING:int = 10;
[Bindable]
private var showButton:Boolean;
private function lockSettingsChanged(e:*):void {
showControlButtons();
}
private function onCreationComplete():void {
showControlButtons();
showPrivateChatButton();
}
public function get padding():int {
return BUTTONS_PADDING;
}
public function userMuted(muted:Boolean):void {
if (muted) {
muteUnmuteBtn.styleName = "videoMutedButtonStyle";
} else {
muteUnmuteBtn.styleName = "videoUnmutedButtonStyle";
}
}
public function updateControlButtons():void {
showControlButtons();
}
private function onKickUserClicked(event:Event):void {
event.stopImmediatePropagation();
var gd:Dispatcher = new Dispatcher();
gd.dispatchEvent(new KickUserEvent(sharerUserID));
}
private function onPrivateChatClicked(event:Event):void {
event.stopImmediatePropagation();
var e:CoreEvent = new CoreEvent(EventConstants.START_PRIVATE_CHAT);
e.message.chatWith = sharerUserID;
var gd:Dispatcher = new Dispatcher();
gd.dispatchEvent(e);
}
private function onSwitchPresenterClicked(event:Event):void {
event.stopImmediatePropagation();
var e:RoleChangeEvent = new RoleChangeEvent(RoleChangeEvent.ASSIGN_PRESENTER);
e.userid = sharerUserID;
e.username = UsersUtil.getUserName(sharerUserID);
var gd:Dispatcher = new Dispatcher();
gd.dispatchEvent(e);
}
private function onMuteUnmuteClicked(event:Event):void {
event.stopImmediatePropagation();
var bu:BBBUser = UsersUtil.getUser(sharerUserID);
if (bu != null) {
var e:VoiceConfEvent = new VoiceConfEvent(VoiceConfEvent.MUTE_USER);
e.userid = bu.userID;
e.mute = ! bu.voiceMuted;
var gd:Dispatcher = new Dispatcher();
gd.dispatchEvent(e);
}
}
private function showControlButtons():void {
var vidOptions:VideoConfOptions = new VideoConfOptions();
displaySwitchPresenterButton(vidOptions.controlsForPresenter);
displayMuteButton(vidOptions.controlsForPresenter);
displayEjectButton(vidOptions.controlsForPresenter);
}
private function displayEjectButton(controlsForPresenter:Boolean):void {
var userOption:UsersOptions = new UsersOptions();
if (! userOption.allowKickUser) {
trace("Kicking user not allowed");
// User kicking not enabled. Just return;
ejectUserBtn.visible = false;
return;
}
trace("Kicking user allowed [" + userOption.allowKickUser + "]");
/**
* Display button if:
* 1. If I am moderator and this window is not me.
* 2. If I am the presenter and display controls for presenter and this window is not me.
*/
if (ejectUserBtn != null) {
if (UsersUtil.amIModerator() && ! UsersUtil.isMe(sharerUserID)) {
ejectUserBtn.visible = true;
ejectUserBtn.toolTip = ResourceUtil.getInstance().getString('bbb.video.controls.ejectUserBtn.toolTip', [UsersUtil.getUserName(sharerUserID)]);
return;
}
if (controlsForPresenter && UsersUtil.amIPresenter() && ! UsersUtil.isMe(sharerUserID)) { /** 2 **/
ejectUserBtn.visible = true;
ejectUserBtn.toolTip = ResourceUtil.getInstance().getString('bbb.video.controls.ejectUserBtn.toolTip', [UsersUtil.getUserName(sharerUserID)]);
return;
}
ejectUserBtn.visible = false;
}
}
private function displaySwitchPresenterButton(controlsForPresenter:Boolean):void {
/**
* Display button if:
* 1. If I am moderator and this user is NOT presenter.
* 2. If I am the presenter and display controls for presenter and this window is not me.
*/
if (switchPresenter != null) {
if (UsersUtil.amIModerator() && (sharerUserID != UsersUtil.getPresenterUserID())) { /** 1. **/
switchPresenter.visible = true;
switchPresenter.toolTip = ResourceUtil.getInstance().getString('bbb.video.controls.switchPresenter.toolTip', [UsersUtil.getUserName(sharerUserID)]);
return;
}
if (controlsForPresenter && UsersUtil.amIPresenter() && ! UsersUtil.isMe(sharerUserID)) { /** 2 **/
switchPresenter.visible = true;
switchPresenter.toolTip = ResourceUtil.getInstance().getString('bbb.video.controls.switchPresenter.toolTip', [UsersUtil.getUserName(sharerUserID)]);
return;
}
switchPresenter.visible = false;
}
}
private function displayMuteButton(controlsForPresenter:Boolean):void {
/**
* Display button if user is joined to voice and:
* 1. I am moderator or presenter and display controls for presenter and user is not locked with mic disabled due to lock
* 2. if this window is me and my mic is not disabled due to lock
* */
if (muteUnmuteBtn != null) {
if (UsersUtil.isUserJoinedToVoice(sharerUserID)) {
var isMe:Boolean = UsersUtil.isMe(sharerUserID);
var userManager:UserManager = UserManager.getInstance();
var conference:Conference = userManager.getConference();
var me:BBBUser = conference.getMyUser();
var allowMuteUnmuteButton:Boolean = false;
if (isMe && !me.disableMyMic) {
allowMuteUnmuteButton = true;
} else if (UsersUtil.amIModerator() || (controlsForPresenter && UsersUtil.amIPresenter()) ) {
allowMuteUnmuteButton = true;
var thisUser:BBBUser = UsersUtil.getUser(sharerUserID);
if (thisUser.userLocked && conference.getLockSettings().getDisableMic()) {
allowMuteUnmuteButton = false;
}
}
if (allowMuteUnmuteButton) {
muteUnmuteBtn.visible = true;
muteUnmuteBtn.toolTip = ResourceUtil.getInstance().getString('bbb.video.controls.muteButton.toolTip', [UsersUtil.getUserName(sharerUserID)]);
} else {
muteUnmuteBtn.visible = false;
}
}
}
}
private function showPrivateChatButton():void {
var chatOptions:ChatOptions = new ChatOptions();
// the first check is to see if the chat module is in the config.xml
if (BBB.getConfigForModule("ChatModule") && chatOptions.privateEnabled && ! UsersUtil.isMe(sharerUserID)) {
privateChatBtn.toolTip = ResourceUtil.getInstance().getString('bbb.video.controls.privateChatBtn.toolTip', [UsersUtil.getUserName(sharerUserID)]);
privateChatBtn.visible = true;
} else {
privateChatBtn.visible = false;
privateChatBtn.includeInLayout = false;
}
}
]]>
</mx:Script>
<mx:Button id="muteUnmuteBtn" visible="false" click="onMuteUnmuteClicked(event)"
width="{BUTTONS_SIZE}" height="{BUTTONS_SIZE}" styleName="videoUnmutedButtonStyle"/>
<mx:Button id="switchPresenter" visible="false" click="onSwitchPresenterClicked(event)"
width="{BUTTONS_SIZE}" height="{BUTTONS_SIZE}" styleName="videoSwitchPresenterButtonStyle" paddingTop="2"/>
<mx:Button id="ejectUserBtn" visible="false" click="onKickUserClicked(event)"
width="{BUTTONS_SIZE}" height="{BUTTONS_SIZE}" styleName="videoEjectUserButtonStyle" paddingTop="2"/>
<mx:Button id="privateChatBtn" click="onPrivateChatClicked(event)"
width="{BUTTONS_SIZE}" height="{BUTTONS_SIZE}" styleName="videoPrivateChatButtonStyle" paddingTop="2"/>
</mx:HBox>

View File

@ -0,0 +1,429 @@
package org.bigbluebutton.modules.videoconf.views
{
import flash.display.DisplayObject;
import flash.net.NetConnection;
import flash.events.Event;
import flash.events.MouseEvent;
import mx.containers.Canvas;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.utils.ObjectUtil;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.modules.videoconf.views.UserGraphicHolder;
import org.bigbluebutton.common.LogUtil;
public class GraphicsWrapper extends Canvas {
private var _options:VideoConfOptions = new VideoConfOptions();
private var priorityWeight:Number = _options.priorityRatio;
private var priorityMode:Boolean = false;
private var priorityItem:DisplayObject = null;
private var _minContentAspectRatio:Number=4/3;
public function GraphicsWrapper() {
percentWidth = percentHeight = 100;
}
override public function addChild(child:DisplayObject):DisplayObject {
throw("You should add the helper functions to add children to this Canvas: addAvatarFor, addVideoFor, addCameraFor");
return null;
}
private function minContentAspectRatio(except:Object = null):Number {
var result:Number = Number.MAX_VALUE;
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
if (item != except && item.contentAspectRatio < result) {
result = item.contentAspectRatio;
}
}
return result;
}
private function calculateCellDimensions(canvasWidth:int, canvasHeight:int, numColumns:int, numRows:int):Object {
var obj:Object = {
width: Math.floor(canvasWidth / numColumns)-5,
height: Math.floor(canvasHeight / numRows)-5
}
if (obj.width / obj.height > _minContentAspectRatio) {
obj.width = Math.floor(obj.height * _minContentAspectRatio);
} else {
obj.height = Math.floor(obj.width / _minContentAspectRatio);
}
return obj;
}
private function calculateOccupiedArea(canvasWidth:int, canvasHeight:int, numColumns:int, numRows:int):Object {
var obj:Object = calculateCellDimensions(canvasWidth, canvasHeight, numColumns, numRows);
obj.occupiedArea = obj.width * obj.height * numChildren;
obj.numColumns = numColumns;
obj.numRows = numRows;
obj.cellAspectRatio = _minContentAspectRatio;
return obj;
}
private function findBestConfiguration(canvasWidth:int, canvasHeight:int, numChildrenInCanvas:int):Object {
var bestConfiguration:Object = {
occupiedArea: 0
}
for (var numColumns:int = 1; numColumns <= numChildrenInCanvas; ++numColumns) {
var numRows:int = Math.ceil(numChildrenInCanvas / numColumns);
var currentConfiguration:Object = calculateOccupiedArea(canvasWidth, canvasHeight, numColumns, numRows);
if (currentConfiguration.occupiedArea > bestConfiguration.occupiedArea) {
bestConfiguration = currentConfiguration;
}
}
return bestConfiguration;
}
private function updateDisplayListHelper(unscaledWidth:Number, unscaledHeight:Number):void {
if (numChildren == 0) {
return;
}
var bestConfiguration:Object = findBestConfiguration(unscaledWidth, unscaledHeight, numChildren);
var numColumns:int = bestConfiguration.numColumns;
var numRows:int = bestConfiguration.numRows;
var cellWidth:int = bestConfiguration.width;
var cellHeight:int = bestConfiguration.height;
var cellAspectRatio:Number = bestConfiguration.cellAspectRatio;
var blockX:int = Math.floor((unscaledWidth - cellWidth * numColumns) / 2);
var blockY:int = Math.floor((unscaledHeight - cellHeight * numRows) / 2);
var itemX:int,
itemY:int,
itemWidth:int,
itemHeight:int;
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
var cellOffsetX:int = 0;
var cellOffsetY:int = 0;
if (item.contentAspectRatio > cellAspectRatio) {
itemWidth = cellWidth;
itemHeight = Math.floor(cellWidth / item.contentAspectRatio);
cellOffsetY = (cellHeight - itemHeight) / 2;
} else {
itemWidth = Math.floor(cellHeight * item.contentAspectRatio);
itemHeight = cellHeight;
cellOffsetX = (cellWidth - itemWidth) / 2;
}
itemX = (i % numColumns) * cellWidth + blockX + cellOffsetX;
itemY = Math.floor(i / numColumns) * cellHeight + blockY + cellOffsetY;
item.setActualSize(itemWidth, itemHeight);
item.move(itemX, itemY);
}
}
private function findPriorityConfiguration(unscaledWidth:Number, unscaledHeight:Number):Object{
var pBestConf:Object = {
numRows: 0,
numColumns: 0,
width: 0,
height: 0
};
var oBestConf:Object = pBestConf;
var isVertSplit:Boolean = false;
if (numChildren > 1){
var pBestConfVer:Object = findBestConfiguration(Math.floor(unscaledWidth * priorityWeight), unscaledHeight, 1);
var pBestConfHor:Object = findBestConfiguration(unscaledWidth, Math.floor(unscaledHeight * priorityWeight), 1);
isVertSplit = (pBestConfVer.occupiedArea > pBestConfHor.occupiedArea);
if (isVertSplit) {
pBestConf = pBestConfVer;
oBestConf = findBestConfiguration(unscaledWidth - pBestConf.width, unscaledHeight, numChildren-1);
} else {
pBestConf = pBestConfHor;
oBestConf = findBestConfiguration(unscaledWidth, unscaledHeight - pBestConf.height, numChildren-1);
}
} else {
pBestConf = findBestConfiguration(unscaledWidth,unscaledHeight,1);
}
return {isVertSplit: isVertSplit, priorConf: pBestConf, otherConf: oBestConf};
}
private function updateDisplayListHelperByPriority(unscaledWidth:Number, unscaledHeight:Number):void {
if (numChildren < 2) {
updateDisplayListHelper(unscaledWidth, unscaledHeight);
return;
}
var bestConf:Object = findPriorityConfiguration(unscaledWidth, unscaledHeight);
var numColumns:int = bestConf.otherConf.numColumns;
var numRows:int = bestConf.otherConf.numRows;
var oWidth:int = bestConf.otherConf.width;
var oHeight:int = bestConf.otherConf.height;
var pWidth:int = bestConf.priorConf.width;
var pHeight:int = bestConf.priorConf.height;
var blockX:int = 0;
var blockY:int = 0;
var cellOffsetX:int = 0;
var cellOffsetY:int = 0;
var itemX:int,
itemY:int,
itemWidth:int,
itemHeight:int;
var item:UserGraphicHolder = priorityItem as UserGraphicHolder;
// set size and position of the prioritized video
if (item.contentAspectRatio > _minContentAspectRatio) {
itemWidth = pWidth;
itemHeight = Math.floor(pWidth / item.contentAspectRatio);
} else {
itemHeight = pHeight;
itemWidth = Math.floor(pHeight * item.contentAspectRatio);
}
if (bestConf.isVertSplit) {
blockX = Math.floor((3*(unscaledWidth - oWidth*numColumns) + itemWidth)/4);
blockY = Math.floor((unscaledHeight - oHeight*numRows)/2);
itemX = Math.floor((unscaledWidth - itemWidth - oWidth*numColumns)/2);
itemY = Math.floor((unscaledHeight - itemHeight)/2);
} else {
blockX = Math.floor((unscaledWidth - oWidth*numColumns)/2);
blockY = Math.floor((3*(unscaledHeight - oHeight*numRows) + itemHeight)/4);
itemX = Math.floor((unscaledWidth - itemWidth)/2);
itemY = Math.floor((unscaledHeight - itemHeight - oHeight*numRows)/2);
}
item.setActualSize(itemWidth, itemHeight);
item.move(itemX, itemY);
// set size and position of the other videos
var nonPriorityIndex:int=0;
for (var curItemIndex:int = 0; curItemIndex < numChildren; ++curItemIndex) {
item = getChildAt(curItemIndex) as UserGraphicHolder;
if (item != priorityItem) {
if (item.contentAspectRatio > _minContentAspectRatio) {
itemWidth = oWidth;
itemHeight = Math.floor(oWidth / item.contentAspectRatio);
} else {
itemHeight = oHeight;
itemWidth = Math.floor(oHeight * item.contentAspectRatio);
}
cellOffsetX = (oWidth - itemWidth)/2;
cellOffsetY = (oHeight - itemHeight)/2;
itemX = (nonPriorityIndex % numColumns) * oWidth + blockX + cellOffsetX;
itemY = Math.floor(nonPriorityIndex / numColumns) * oHeight + blockY + cellOffsetY;
nonPriorityIndex++;
item.setActualSize(itemWidth, itemHeight);
item.move(itemX, itemY);
}
}
}
override protected function updateDisplayList(w:Number, h:Number):void {
trace("[GraphicsWrapper::updateDisplayList]");
super.updateDisplayList(w, h);
if (priorityMode) {
updateDisplayListHelperByPriority(w, h);
} else {
updateDisplayListHelper(w, h);
}
}
/*
override public function validateDisplayList():void {
super.validateDisplayList();
if (priorityMode) {
updateDisplayListHelperByPriority(this.width, this.height);
} else {
updateDisplayListHelper(this.width, this.height);
}
}
*/
public function addAvatarFor(userId:String):void {
if (! UsersUtil.hasUser(userId)) return;
var graphic:UserGraphicHolder = new UserGraphicHolder();
graphic.userId = userId;
graphic.addEventListener(FlexEvent.CREATION_COMPLETE, function(event:FlexEvent):void {
graphic.loadAvatar(_options);
onChildAdd(event);
});
graphic.addEventListener(MouseEvent.CLICK, onVBoxClick);
graphic.addEventListener(FlexEvent.REMOVE, onChildRemove);
super.addChild(graphic);
}
private function addVideoForHelper(userId:String, connection:NetConnection, streamName:String):void {
trace("[GraphicsWrapper:addVideoForHelper] streamName " + streamName);
var graphic:UserGraphicHolder = new UserGraphicHolder();
graphic.userId = userId;
graphic.addEventListener(FlexEvent.CREATION_COMPLETE, function(event:FlexEvent):void {
graphic.loadVideo(_options, connection, streamName);
onChildAdd(event);
});
graphic.addEventListener(MouseEvent.CLICK, onVBoxClick);
graphic.addEventListener(FlexEvent.REMOVE, onChildRemove);
super.addChild(graphic);
}
public function addVideoFor(userId:String, connection:NetConnection):void {
var user:BBBUser = UsersUtil.getUser(userId);
if (user == null) return;
var streamNames:Array = user.streamNames;
for each (var streamName:String in streamNames) {
if (user.viewingStream.indexOf(streamName) == -1) {
addVideoForHelper(user.userID, connection, streamName);
}
}
}
private function addCameraForHelper(userId:String, camIndex:int, videoProfile:VideoProfile):void {
var graphic:UserGraphicHolder = new UserGraphicHolder();
graphic.userId = userId;
graphic.addEventListener(FlexEvent.CREATION_COMPLETE, function(event:FlexEvent):void {
graphic.loadCamera(_options, camIndex, videoProfile);
onChildAdd(event);
});
graphic.addEventListener(MouseEvent.CLICK, onVBoxClick);
graphic.addEventListener(FlexEvent.REMOVE, onChildRemove);
super.addChild(graphic);
}
private function onChildAdd(event:FlexEvent):void {
_minContentAspectRatio = minContentAspectRatio();
invalidateDisplayList();
}
private function onChildRemove(event:FlexEvent):void {
if (priorityMode && event.target == priorityItem) {
priorityMode = false;
priorityItem = null;
}
_minContentAspectRatio = minContentAspectRatio(event.target);
invalidateDisplayList();
}
protected function onVBoxClick(event:MouseEvent):void {
var item:UserGraphicHolder = event.currentTarget as UserGraphicHolder;
// when the user clicks to close the video, the click event is fired but the window
// is no longer child of this class, so we need to test it first
if (this.contains(item)) {
priorityMode = !priorityMode || item != priorityItem;
if (priorityMode) {
priorityItem = item;
}
invalidateDisplayList();
}
}
public function addCameraFor(userId:String, camIndex:int, videoProfile:VideoProfile):void {
if (! UsersUtil.hasUser(userId)) return;
var alreadyPublishing:Boolean = false;
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
if (item.user && item.user.userID == userId && item.visibleComponent is UserVideo && item.video.camIndex == camIndex) {
alreadyPublishing = true;
break;
}
}
if (!alreadyPublishing) {
addCameraForHelper(userId, camIndex, videoProfile);
}
}
private function removeChildHelper(child:UserGraphicHolder):void {
child.shutdown();
if (contains(child)) {
removeChild(child);
}
}
public function removeAvatarFor(userId:String):void {
trace("[GraphicsWrapper:removeAvatarFor] userId " + userId);
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
if (item.user && item.user.userID == userId && item.visibleComponent is UserAvatar) {
trace("[GraphicsWrapper:removeAvatarFor] removing graphic");
removeChildHelper(item);
// recursive call to remove all avatars for userId
removeAvatarFor(userId);
break;
}
}
}
public function removeVideoByCamIndex(userId:String, camIndex:int):String {
trace("[GraphicsWrapper:removeVideoByCamIndex] userId " + userId + " camIndex " + camIndex);
var streamName:String = "";
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
if (item.user && item.user.userID == userId && item.visibleComponent is UserVideo && item.video.camIndex == camIndex) {
streamName = item.video.streamName;
removeChildHelper(item);
break;
}
}
return streamName;
}
public function removeVideoByStreamName(userId:String, streamName:String):int {
var camIndex:int = -1;
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
if (item.user && item.user.userID == userId && item.visibleComponent is UserVideo && item.video.streamName == streamName) {
camIndex = item.video.camIndex;
removeChildHelper(item);
break;
}
}
return camIndex;
}
public function removeGraphicsFor(userId:String):void {
trace("[GraphicsWrapper:removeGraphicsFor] userId " + userId);
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
if (item.user && item.user.userID == userId) {
trace("[GraphicsWrapper:removeGraphicsFor] removing graphic");
removeChildHelper(item);
// recursive call to remove all graphics for userId
removeGraphicsFor(userId);
break;
}
}
}
public function hasGraphicsFor(userId:String):Boolean {
for (var i:int = 0; i < numChildren; ++i) {
var item:UserGraphicHolder = getChildAt(i) as UserGraphicHolder;
if (item.user && item.user.userID == userId) {
return true;
}
}
return false;
}
public function shutdown():void {
while (numChildren > 0) {
var item:UserGraphicHolder = getChildAt(0) as UserGraphicHolder;
removeChildHelper(item);
}
}
}
}

View File

@ -1,488 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<pubVid:VideoWindowItf
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:pubVid="org.bigbluebutton.modules.videoconf.business.*"
initialize="init()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
styleNameFocus="videoPublishStyleFocus"
styleNameNoFocus="videoPublishStyleNoFocus"
creationComplete="onCreationComplete()"
width="{defaultWidth + 6}" height="{defaultHeight + 6}"
xmlns:mate="http://mate.asfusion.com/"
resize="onResize()"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
layout="absolute">
<mate:Listener type="{BBBEvent.USER_VOICE_MUTED}" method="handleUserVoiceMutedEvent" />
<mate:Listener type="{EventConstants.USER_TALKING}" method="handleUserTalkingEvent" />
<mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" />
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="handleMadePresenterEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_JOINED}" method="handleNewRoleEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_LEFT}" method="handleNewRoleEvent" />
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
<mate:Listener type="{ShortcutEvent.REMOTE_FOCUS_WEBCAM}" method="remoteFocus" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
import mx.events.ResizeEvent;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.Role;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.common.events.ToolbarButtonEvent;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.ShortcutEvent;
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.business.TalkingButtonOverlay;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
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.model.VideoConfOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
[Bindable] private var defaultWidth:Number = 320;
[Bindable] private var defaultHeight:Number = 240;
public var camIndex:int = 0;
private var camWidth:Number = 320;
private var camHeight:Number = 240;
private var _camera:Camera = null;
public var quality:Number = 0;
public var chromePermissionDenied:Boolean = false;
// 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
// won't be able to view the webcam.
private var autoPublishTimer:Timer = null;
// Timer used to enable the start publishing button, only after get
// any activity on the camera. It avoids the problem of publishing
// a blank video
private var _activationTimer:Timer = null;
private var _waitingForActivation:Boolean = false;
static private var _cameraAccessDenied:Boolean = false;
[Bindable] public var videoOptions:VideoConfOptions;
[Bindable] public var baseIndex:int;
[Bindable] public var glowColor:String = "";
[Bindable] public var glowBlurSize:Number = 0;
private function init():void{
videoOptions = new VideoConfOptions();
baseIndex = videoOptions.baseTabIndex;
}
private var windowType:String = "PublishWindowType";
override public function getWindowType():String {
return windowType;
}
private function onCreationComplete():void{
this.glowColor = videoOptions.glowColor;
this.glowBlurSize = videoOptions.glowBlurSize;
_videoHolder = new UIComponent();
_videoHolder.width = camWidth;
_videoHolder.height = camHeight;
this.addChild(_videoHolder);
this.minWidth = _minWidth;
this.minHeight = _minHeight;
maximizeRestoreBtn.visible = false;
this.resizable = false;
this.visible = videoOptions.publishWindowVisible;
this.showCloseButton = videoOptions.showCloseButton;
if (videoOptions.autoStart) {
/*
* Need to have a timer to trigger auto-publishing of webcam.
*/
autoPublishTimer = new Timer(3000, 1);
autoPublishTimer.addEventListener(TimerEvent.TIMER, autopublishTimerHandler);
autoPublishTimer.start();
}
titleBarOverlay.tabIndex = baseIndex;
titleBarOverlay.accessibilityName = ResourceUtil.getInstance().getString('bbb.video.publish.titleBar');
closeBtn.focusEnabled = true;
closeBtn.tabIndex = baseIndex+3;
addEventListener(MDIWindowEvent.RESIZE_START, onResizeStart);
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEnd);
addEventListener(MouseEvent.MOUSE_OVER, showButtons);
addEventListener(MouseEvent.MOUSE_OUT, hideButtons);
addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
// start the camera preview
updateCamera();
}
private function remoteFocus(e:ShortcutEvent):void{
focusManager.setFocus(closeBtn);
}
private function autopublishTimerHandler(event:TimerEvent):void {
startPublishing();
autoPublishTimer.stop();
}
private function handleMadePresenterEvent(event:MadePresenterEvent):void {
trace("******** PublishWindow: HandleMadePresenter event *********");
updateControlButtons();
}
private function handleSwitchedPresenterEvent(event:SwitchedPresenterEvent):void {
trace("******** PublishWindow: handleSwitchedPresenterEvent event *********");
updateControlButtons();
}
private function handleNewRoleEvent(event:Event):void {
updateControlButtons();
}
private function handleUserVoiceMutedEvent(event:BBBEvent):void {
if (event.payload.userID == userID) {
userMuted(event.payload.muted);
}
}
private function handleUserTalkingEvent(event:CoreEvent):void {
if (event.message.userID == userID) {
if (event.message.talking) {
notTalkingEffect.end();
talkingEffect.play([this]);
simulateClick();
} else {
talkingEffect.end();
notTalkingEffect.play([this]);
}
}
}
private function updateCamera():void {
stopCamera();
if (Camera.names.length == 0) {
showWarning('bbb.video.publish.hint.noCamera');
return;
}
_camera = Camera.getCamera();
if (_camera == null) {
showWarning('bbb.video.publish.hint.cantOpenCamera');
return;
}
_camera.setMotionLevel(5, 1000);
if (_camera.muted) {
if (_cameraAccessDenied) {
onCameraAccessDisallowed();
return;
} else {
showWarning('bbb.video.publish.hint.waitingApproval');
}
} else {
// if the camera isn't muted, that is because the user has
// previously allowed the camera capture on the flash privacy box
onCameraAccessAllowed();
}
_camera.addEventListener(ActivityEvent.ACTIVITY, onActivityEvent);
_camera.addEventListener(StatusEvent.STATUS, onStatusEvent);
_camera.setKeyFrameInterval(videoOptions.camKeyFrameInterval);
_camera.setMode(camWidth, camHeight, videoOptions.camModeFps);
_camera.setQuality(videoOptions.camQualityBandwidth, videoOptions.camQualityPicture);
if (_camera.width != camWidth || _camera.height != camHeight) {
trace("Resolution " + camWidth + "x" + camHeight + " is not supported, using " + _camera.width + "x" + _camera.height + " instead");
setResolution(_camera.width, _camera.height);
}
_video = new Video();
_video.attachCamera(_camera);
_video.smoothing = true;
if (aspectRatio > _videoHolder.width / _videoHolder.height) {
_video.width = _videoHolder.width;
_video.height = _videoHolder.width / aspectRatio;
_video.x = 0;
_video.y = (_videoHolder.height - _video.height) / 2;
} else {
_video.width = _videoHolder.height * aspectRatio;
_video.height = _videoHolder.height;
_video.x = (_videoHolder.width - _video.width) / 2;
_video.y = 0;
}
_videoHolder.addChild(_video);
}
private function onActivityEvent(e:ActivityEvent):void {
if (_waitingForActivation && e.activating) {
trace("Cam activity event: waitingForActivation = [" + _waitingForActivation + "] activating = [" + e.activating + "]");
_activationTimer.stop();
showWarning('bbb.video.publish.hint.videoPreview', false, "0xFFFF00");
_waitingForActivation = false;
trace("Starting auto-publisher timer.");
autoPublishTimer = new Timer(3000, 1);
autoPublishTimer.addEventListener(TimerEvent.TIMER, autopublishTimerHandler);
autoPublishTimer.start();
}
}
private function onStatusEvent(e:StatusEvent):void {
if (e.code == "Camera.Unmuted") {
onCameraAccessAllowed();
// this is just to overwrite the message of waiting for approval
showWarning('bbb.video.publish.hint.openingCamera');
} else if (e.code == "Camera.Muted") {
onCameraAccessDisallowed();
}
}
private function onCameraAccessAllowed():void {
// set timer to ensure that the camera activates. If not, it might be in use by another application
_waitingForActivation = true;
if (_activationTimer != null) {
_activationTimer.stop();
}
_activationTimer = new Timer(10000, 1);
_activationTimer.addEventListener(TimerEvent.TIMER, activationTimeout);
_activationTimer.start();
}
private function onCameraAccessDisallowed():void {
showWarning('bbb.video.publish.hint.cameraDenied');
_cameraAccessDenied = true;
}
private function activationTimeout(e:TimerEvent):void {
if(chromePermissionDenied)
{
showWarning('bbb.video.publish.hint.cameraDenied');
return;
}
else
{
showWarning('bbb.video.publish.hint.cameraIsBeingUsed');
}
// it will try to reopen the camera after the timeout
updateCamera();
}
private function startPublishing():void{
if (_camera == null) return;
if (autoPublishTimer != null) {
autoPublishTimer.stop();
}
showWarning('bbb.video.publish.hint.publishing', true, "0xFFFF00");
var e:StartBroadcastEvent = new StartBroadcastEvent();
e.stream = this.streamName;
e.camera = _camera;
dispatchEvent(e);
// Store the userid for the publisher. This allows us to control
// the user's status from the video window
userID = UsersUtil.getMyUserID();
createButtons();
addControlButtons();
}
private var _isClosing:Boolean = false;
override public function close(event:MouseEvent=null):void{
trace("PublishWindow:: closing");
if (!_isClosing) {
_isClosing = true;
stopPublishing();
trace("Dispatching ClosePublishWindowEvent event");
var gDispatcher:Dispatcher = new Dispatcher();
gDispatcher.dispatchEvent(new ClosePublishWindowEvent());
}
}
private function stopCamera():void {
trace("PublishWindow:: stopping camera");
_camera = null;
if (_video != null) {
trace("PublishWindow:: removing video from display");
_videoHolder.removeChild(_video);
_video.attachCamera(null);
_video.clear();
_video = null;
}
}
private function stopPublishing():void{
trace("PublishWindow::stopPublishing");
stopCamera();
var e:StopBroadcastEvent = new StopBroadcastEvent()
e.stream = streamName;
dispatchEvent(e);
}
override protected function onResize():void {
super.onResize();
if (_videoHolder != null) {
fitVideoToWindow();
_video.x = (this.width - PADDING_HORIZONTAL - _video.width) / 2;
_video.y = (this.height - PADDING_VERTICAL - _video.height) / 2;
}
}
private function fitVideoToWindow():void {
if (_videoHolder.width - PADDING_HORIZONTAL < _videoHolder.height - PADDING_VERTICAL) {
fitToHeightAndAdjustWidthToMaintainAspectRatio();
} else {
fitToWidthAndAdjustHeightToMaintainAspectRatio();
}
}
private function videoIsSmallerThanWindow():Boolean {
return (camWidth < _videoHolder.width - PADDING_HORIZONTAL) && (camHeight < _videoHolder.height - PADDING_VERTICAL);
}
private function fitToWidthAndAdjustHeightToMaintainAspectRatio():void {
var aspect:Number = camHeight / camWidth;
_video.width = _videoHolder.width - PADDING_HORIZONTAL;
// Maintain aspect-ratio
_video.height = _video.width * aspect;
}
private function fitToHeightAndAdjustWidthToMaintainAspectRatio():void {
var aspect:Number = camWidth / camHeight;
_video.height = _videoHolder.height - PADDING_VERTICAL;
// Maintain aspect-ratio
_video.width = _video.height * aspect;
}
public function setResolution(width:int, height:int):void {
camWidth = width;
camHeight = height;
setAspectRatio(camWidth, camHeight);
/**
* Add timestamp to create a unique stream name. This way we can record
* stream without overwriting previously recorded streams.
*/
var d:Date = new Date();
var curTime:Number = d.getTime();
var uid:String = UserManager.getInstance().getConference().getMyUserId();
var res:String = camWidth + "x" + camHeight;
this.streamName = res.concat("-" + uid) + "-" + curTime;
}
private function isPresenter():Boolean{
if (UsersUtil.amIModerator() || UsersUtil.amIPresenter()) return true;
else return false;
}
private function closeWindow(e:CloseAllWindowsEvent):void{
trace("PublishWindow::closeWindow");
stopCamera();
}
private var hideWarningTimer:Timer = null;
private function showWarning(resourceName:String, autoHide:Boolean=false, color:String="0xFF0000"):void {
const text:String = ResourceUtil.getInstance().getString(resourceName);
if (hideWarningTimer != null)
hideWarningTimer.stop();
if (autoHide) {
hideWarningTimer = new Timer(3000, 1);
hideWarningTimer.addEventListener(TimerEvent.TIMER, hideWarning);
hideWarningTimer.start();
}
// bring the label to front
setChildIndex(lblWarning, getChildren().length - 1);
lblWarning.text = text;
lblWarning.setStyle("color", color);
lblWarning.visible = true;
LogUtil.debug("Showing warning: " + text);
}
private function hideWarning(e:TimerEvent):void {
lblWarning.visible = false;
}
]]>
</mx:Script>
<mx:Glow id="talkingEffect" duration="500" alphaFrom="1.0" alphaTo="0.3"
blurXFrom="0.0" blurXTo="{glowBlurSize}" blurYFrom="0.0" blurYTo="{glowBlurSize}" color="{glowColor}"/>
<mx:Glow id="notTalkingEffect" duration="500" alphaFrom="0.3" alphaTo="1.0"
blurXFrom="{glowBlurSize}" blurXTo="0.0" blurYFrom="{glowBlurSize}" blurYTo="0.0" color="{glowColor}"/>
<mx:Fade id="dissolveOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
<mx:Fade id="dissolveIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>
<mx:Text id="lblWarning" width="100%" textAlign="center" fontSize="14" fontWeight="bold" y="{this.height - lblWarning.height - 30}"
visible="false" selectable="false" hideEffect="{dissolveOut}" showEffect="{dissolveIn}"/>
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
</pubVid:VideoWindowItf>

View File

@ -1,168 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:Button xmlns:mx="http://www.adobe.com/2006/mxml" styleName="webcamDefaultButtonStyle"
xmlns:mate="http://mate.asfusion.com/"
click="openPublishWindow()" creationComplete="init()"
mouseOver = "mouseOverHandler(event)"
mouseOut = "mouseOutHandler(event)"
height="24"
toolTip="{this.selected ? ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.stop') : ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.start')}"
visible="false"
implements="org.bigbluebutton.common.IBbbToolbarComponent">
<mate:Listener type="{ShortcutEvent.SHARE_WEBCAM}" method="remoteClick" />
<mate:Listener type="{BBBEvent.CAM_SETTINGS_CLOSED}" method="handleCamSettingsClosedEvent"/>
<mate:Listener type="{ShareCameraRequestEvent.SHARE_CAMERA_REQUEST}" receive="enabled=false" />
<mate:Listener type="{LockControlEvent.CHANGED_LOCK_SETTINGS}" method="lockSettingsChanged" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.LockControlEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.ShortcutEvent;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.main.model.users.Conference;
import org.bigbluebutton.main.views.MainToolbar;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
public const OFF_STATE:Number = 0;
public const ON_STATE:Number = 1;
public const STOP_PUBLISHING:Number = 0;
public const START_PUBLISHING:Number = 1;
private var _currentState:Number = OFF_STATE;
private var dispatcher:Dispatcher;
public function lockSettingsChanged(e:*):void{
if (UsersUtil.amIModerator() || UsersUtil.amIPresenter()){
// Ignore lock setting changes as
// or presenter we are moderator.
return;
}
var userManager:UserManager = UserManager.getInstance();
var conference:Conference = userManager.getConference();
var me:BBBUser = conference.getMyUser();
this.visible = !me.disableMyCam;
this.includeInLayout = !me.disableMyCam;
}
private function init():void{
dispatcher = new Dispatcher();
this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.start');
this.styleName = "webcamDefaultButtonStyle";
this.enabled = true;
this.selected = false;
_currentState = OFF_STATE;
lockSettingsChanged(null);
}
public function set isPresenter(presenter:Boolean):void {
visible = presenter;
}
public function remoteClick(e:ShortcutEvent):void{
openPublishWindow();
dispatchEvent(new ShortcutEvent(ShortcutEvent.REMOTE_FOCUS_WEBCAM));
}
public function publishingStatus(status:Number):void {
if(status == START_PUBLISHING) {
_currentState = ON_STATE;
//this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.stop');
this.styleName = "webcamOnButtonStyle";
this.enabled = true;
this.selected = true;
}
else {
_currentState = OFF_STATE;
this.styleName = "webcamDefaultButtonStyle";
this.enabled = true;
this.selected = false;
}
}
private function openPublishWindow():void{
if(_currentState == ON_STATE) {
trace("Close window");
_currentState = OFF_STATE;
this.styleName = "webcamDefaultButtonStyle";
this.selected = false;
dispatchEvent(new ClosePublishWindowEvent());
} else {
trace("Share camera");
_currentState = ON_STATE;
this.styleName = "webcamOnButtonStyle";
this.selected = true;
dispatchEvent(new ShareCameraRequestEvent());
}
}
private function handleCamSettingsClosedEvent(e:BBBEvent):void {
this.setFocus();
if(e.payload['clicked'] == "cancel"){
this.enabled = true;
this.selected = false;
_currentState = OFF_STATE;
this.styleName = "webcamDefaultButtonStyle";
}
}
private function mouseOverHandler(event:MouseEvent):void {
if(_currentState == ON_STATE)
this.styleName = "webcamOffButtonStyle";
else
this.styleName = "webcamOnButtonStyle";
}
private function mouseOutHandler(event:MouseEvent):void {
if(_currentState == ON_STATE)
this.styleName = "webcamOnButtonStyle";
else
this.styleName = "webcamDefaultButtonStyle";
}
public function getAlignment():String{
return MainToolbar.ALIGN_LEFT;
}
public function theory():String{
return "Webcam button";
}
]]>
</mx:Script>
</mx:Button>

View File

@ -0,0 +1,255 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:PopUpButton xmlns:mx="http://www.adobe.com/2006/mxml" styleName="webcamDefaultButtonStyle"
xmlns:mate="http://mate.asfusion.com/"
click="openPublishWindow()" creationComplete="init()"
mouseOver = "mouseOverHandler(event)"
mouseOut = "mouseOutHandler(event)"
height="24"
toolTip="{this.selected ? ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.stop') : ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.start')}"
visible="false"
enabled="true"
selected="false"
implements="org.bigbluebutton.common.IBbbToolbarComponent">
<mate:Listener type="{ShortcutEvent.SHARE_WEBCAM}" method="remoteClick" />
<mate:Listener type="{BBBEvent.CAM_SETTINGS_CLOSED}" method="handleCamSettingsClosedEvent"/>
<mate:Listener type="{ShareCameraRequestEvent.SHARE_CAMERA_REQUEST}" receive="enabled=false" />
<mate:Listener type="{LockControlEvent.CHANGED_LOCK_SETTINGS}" method="lockSettingsChanged" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import mx.controls.Menu;
import mx.events.MenuEvent;
import mx.styles.StyleManager;
import mx.styles.IStyleManager2;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.core.events.LockControlEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.ShortcutEvent;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.main.model.users.Conference;
import org.bigbluebutton.main.views.MainToolbar;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopShareCameraRequestEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
public const OFF_STATE:Number = 0;
public const ON_STATE:Number = 1;
public const STOP_PUBLISHING:Number = 0;
public const START_PUBLISHING:Number = 1;
private var _currentState:Number = OFF_STATE;
private var dp:Object = [];
private var dataMenu:Menu;
public var numberOfCamerasOff:int = 0;
private var dispatcher:Dispatcher;
public function lockSettingsChanged(e:*):void{
var userManager:UserManager = UserManager.getInstance();
var conference:Conference = userManager.getConference();
var me:BBBUser = conference.getMyUser();
this.visible = !me.disableMyCam;
this.includeInLayout = !me.disableMyCam;
}
private function init():void{
dispatcher = new Dispatcher();
numberOfCamerasOff = Camera.names.length;
for(var i:int = 0; i < Camera.names.length; i++) {
dp.push({label: Camera.names[i], status: OFF_STATE});
}
dataMenu = Menu.createMenu(this, dp, false);
dataMenu.addEventListener("itemClick", changeHandler);
dataMenu.addEventListener("mouseOver", mouseOverHandler);
dataMenu.addEventListener("mouseOut", mouseOutHandler);
dataMenu.iconFunction = webcamMenuIcon;
this.popUp = dataMenu;
switchStateToNormal();
}
private function webcamMenuIcon(item:Object):Class {
var styleManager:IStyleManager2 = StyleManager.getStyleManager(null);
if(item.status == ON_STATE) {
return styleManager.getStyleDeclaration(".webcamOnButtonStyle").getStyle("icon");
}
else {
return styleManager.getStyleDeclaration(".webcamDefaultButtonStyle").getStyle("icon");
}
}
private function switchStateToNormal():void {
this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.start');
this.styleName = "webcamDefaultButtonStyle";
this.enabled = true;
this.selected = false;
_currentState = OFF_STATE;
lockSettingsChanged(null);
}
public function set isPresenter(presenter:Boolean):void {
visible = includeInLayout = presenter;
}
public function remoteClick(e:ShortcutEvent):void{
openPublishWindow();
dispatchEvent(new ShortcutEvent(ShortcutEvent.REMOTE_FOCUS_WEBCAM));
}
public function publishingStatus(status:Number, camID:Number = -1):void {
if(status == START_PUBLISHING) {
_currentState = ON_STATE;
//this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.stop');
this.styleName = "webcamOnButtonStyle";
this.enabled = true;
this.selected = true;
}
else {
if(camID != -1) {
dp[camID].status = OFF_STATE;
if(numberOfCamerasOff < Camera.names.length)
numberOfCamerasOff++;
}
if(numberOfCamerasOff == Camera.names.length) {
switchStateToNormal();
}
}
var evt:BBBEvent = new BBBEvent("EnableToolbarPopupButton");
dispatchEvent(evt);
dataMenu.dataProvider = dp;
}
private function openPublishWindow():void{
this.enabled = false;
if(_currentState == ON_STATE) {
trace("[ToolbarPopupButton:openPublishWindow] Close window");
switchStateToNormal();
var stopShareCameraRequestEvent:StopShareCameraRequestEvent = new StopShareCameraRequestEvent(StopShareCameraRequestEvent.STOP_SHARE_ALL_CAMERA_REQUEST);
dispatchEvent(stopShareCameraRequestEvent);
this.enabled = true;
} else {
trace("Share camera");
if(numberOfCamerasOff > 0)
numberOfCamerasOff--;
_currentState = ON_STATE;
this.styleName = "webcamOnButtonStyle";
this.selected = true;
var shareCameraRequestEvent:ShareCameraRequestEvent = new ShareCameraRequestEvent();
shareCameraRequestEvent.camerasArray = dp;
dispatchEvent(shareCameraRequestEvent);
}
}
private function handleCamSettingsClosedEvent(e:BBBEvent):void {
this.setFocus();
this.enabled = true;
if(e.payload['clicked'] == "cancel") {
if(numberOfCamerasOff < Camera.names.length)
numberOfCamerasOff++;
if(numberOfCamerasOff == Camera.names.length) {
switchStateToNormal();
}
}
}
private function mouseOverHandler(event:MouseEvent):void {
if(_currentState == ON_STATE)
this.styleName = "webcamOffButtonStyle";
else
this.styleName = "webcamOnButtonStyle";
this.selected = false;
}
private function mouseOutHandler(event:MouseEvent):void {
if(_currentState == ON_STATE)
this.styleName = "webcamOnButtonStyle";
else
this.styleName = "webcamDefaultButtonStyle";
this.selected = false;
}
public function getAlignment():String{
return MainToolbar.ALIGN_LEFT;
}
public function theory():String{
return "Webcam button";
}
private function changeHandler(event:MenuEvent):void {
if(dp[event.index].status == ON_STATE) {
var stopShareCameraRequestEvent:StopShareCameraRequestEvent = new StopShareCameraRequestEvent();
stopShareCameraRequestEvent.camId = event.index;
dispatchEvent(stopShareCameraRequestEvent);
}
else {
this.enabled = false;
if(numberOfCamerasOff > 0)
numberOfCamerasOff--;
var shareCameraRequestEvent:ShareCameraRequestEvent = new ShareCameraRequestEvent();
shareCameraRequestEvent.defaultCamera = String(event.index);
shareCameraRequestEvent.camerasArray = dp;
dispatchEvent(shareCameraRequestEvent);
}
}
public function setCamAsInactive(camIndex:int):void {
if(camIndex != -1) {
dp[camIndex].status = OFF_STATE;
dataMenu.dataProvider = dp;
}
}
public function setAllCamAsInactive():void {
numberOfCamerasOff = Camera.names.length;
for(var i:int = 0; i < Camera.names.length; i++) {
setCamAsInactive(i);
}
switchStateToNormal();
}
public function setCamAsActive(camIndex:int):void {
if(camIndex != -1) {
dp[camIndex].status = ON_STATE;
dataMenu.dataProvider = dp;
}
}
]]>
</mx:Script>
</mx:PopUpButton>

View File

@ -0,0 +1,42 @@
package org.bigbluebutton.modules.videoconf.views
{
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import mx.events.FlexEvent;
import mx.utils.ObjectUtil;
public class UserAvatar extends UserGraphic {
private var _imageLoader:Loader = null;
private var _completed:Boolean;
public function UserAvatar() {
super();
}
public function load(avatarUrl:String):void {
_imageLoader = new Loader;
_completed = false;
_imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadingComplete);
addChild(_imageLoader);
var request:URLRequest = new URLRequest(avatarUrl);
_imageLoader.load(request);
}
private function onLoadingComplete(event:Event):void {
_completed = true;
setOriginalDimensions(_imageLoader.width, _imageLoader.height);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (_completed) {
updateDisplayListHelper(unscaledWidth, unscaledHeight, _imageLoader);
}
}
}
}

View File

@ -0,0 +1,96 @@
package org.bigbluebutton.modules.videoconf.views
{
import flash.display.DisplayObject;
import mx.containers.Canvas;
import mx.core.UIComponent;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
public class UserGraphic extends UIComponent {
protected var _user:BBBUser = null;
protected var _options:VideoConfOptions;
protected var _origWidth:Number = 320;
protected var _origHeight:Number = 240;
protected var _background:Canvas;
protected const BORDER_THICKNESS:int = 0;
public function UserGraphic() {
super();
_background = new Canvas();
_background.setStyle("backgroundColor", "white");
_background.setStyle("borderStyle", "solid");
_background.setStyle("borderColor", "#000000");
_background.setStyle("borderThickness", BORDER_THICKNESS);
_background.horizontalScrollPolicy = "off";
_background.verticalScrollPolicy = "off";
addChild(_background);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
_background.setActualSize(unscaledWidth, unscaledHeight);
}
protected function setOriginalDimensions(width:Number, height:Number):void {
_origWidth = width;
_origHeight = height;
invalidateDisplayList();
}
public function get aspectRatio():Number {
return _origWidth / _origHeight;
}
protected function updateDisplayListHelper(unscaledWidth:Number, unscaledHeight:Number, object:DisplayObject):void {
if (object == null) {
return;
}
var object_x:Number;
var object_y:Number;
var object_width:Number;
var object_height:Number;
unscaledHeight -= BORDER_THICKNESS * 2;
unscaledWidth -= BORDER_THICKNESS * 2;
if (unscaledWidth / unscaledHeight > aspectRatio) {
object_height = unscaledHeight;
object_width = Math.ceil(unscaledHeight * aspectRatio);
object_y = BORDER_THICKNESS;
object_x = Math.floor((unscaledWidth - object.width) / 2);
} else {
object_width = unscaledWidth;
object_height = Math.ceil(unscaledWidth / aspectRatio);
object_x = BORDER_THICKNESS;
object_y = Math.floor((unscaledHeight - object.height) / 2);
}
object.x = object_x;
object.y = object_y;
object.width = object_width;
object.height = object_height;
}
public function set user(value:BBBUser):void {
_user = value;
}
public function get user():BBBUser {
return _user;
}
public function set options(value:VideoConfOptions):void {
_options = value;
}
public function get options():VideoConfOptions {
return _options;
}
}
}

View File

@ -0,0 +1,317 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:mate="http://mate.asfusion.com/"
xmlns:views="org.bigbluebutton.modules.videoconf.views.*"
initialize="init()"
creationComplete="onCreationComplete()"
backgroundColor="white" width="320" height="240"
mouseOver="onCanvasMouseOver()" mouseOut="onCanvasMouseOut()" >
<mate:Listener type="{EventConstants.USER_TALKING}" method="handleUserTalkingEvent" />
<mate:Listener type="{PresenterStatusEvent.PRESENTER_NAME_CHANGE}" method="handlePresenterChangedEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_LEFT}" method="handleUserVoiceChangedEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_MUTED}" method="handleUserVoiceChangedEvent" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.events.VoiceConfEvent;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.main.events.PresenterStatusEvent;
import org.bigbluebutton.main.model.users.BBBUser;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
[Bindable]
private var _rolledOverMuteBtn:Boolean = false;
[Bindable]
private var _rolledOverCanvas:Boolean = false;
[Bindable]
private var _username:String = "";
[Bindable]
private var _me:Boolean = false;
private var _dispatcher:Dispatcher = new Dispatcher();
private var _images:Images = new Images();
private var _user:BBBUser = null;
private var _hideMuteBtnTimer:Timer;
protected function init():void {
_hideMuteBtnTimer = new Timer(500, 1);
_hideMuteBtnTimer.addEventListener(TimerEvent.TIMER, onHideMuteBtnTimerComplete);
}
protected function onCreationComplete():void {
}
public function set userId(value:String):void {
_user = UsersUtil.getUser(value);
}
public function loadAvatar(options:VideoConfOptions):void {
avatar.user = _user;
avatar.options = options;
avatar.load(UsersUtil.getAvatarURL());
avatarVisibility = true;
setUserProperties();
}
public function loadCamera(options:VideoConfOptions, camIndex:int, videoProfile:VideoProfile):void {
video.user = _user;
video.options = options;
video.publish(camIndex, videoProfile);
videoVisibility = true;
setUserProperties();
}
public function loadVideo(options:VideoConfOptions, connection:NetConnection, streamName:String):void {
video.user = _user;
video.options = options;
video.view(connection, streamName);
videoVisibility = true;
setUserProperties();
}
private function setUserProperties():void {
_username = user.name;
_me = user.me;
updateButtons();
}
public function get visibleComponent():UserGraphic {
if (avatar.visible) {
return avatar;
} else if (video.visible) {
return video;
} else {
return null;
}
}
public function get contentAspectRatio():Number {
if (visibleComponent) {
return visibleComponent.aspectRatio;
} else {
return 320 / 240;
}
}
public function get user():BBBUser {
return _user;
}
public function shutdown():void {
trace("[UserGraphicHolder:shutdown]");
video.shutdown();
}
private function set avatarVisibility(value:Boolean):void {
avatar.visible = avatar.includeInLayout = value;
video.visible = video.includeInLayout = !value;
}
private function set videoVisibility(value:Boolean):void {
avatarVisibility = !value;
}
private function hasPermissionToMute():Boolean {
return (_me || UsersUtil.amIModerator());
}
private function onMuteBtnClick(event:MouseEvent):void {
if (user && user.voiceJoined && hasPermissionToMute()) {
var e:VoiceConfEvent = new VoiceConfEvent(VoiceConfEvent.MUTE_USER);
e.userid = user.userID;
e.mute = !user.voiceMuted;
_dispatcher.dispatchEvent(e);
}
event.stopPropagation();
}
private function onMuteBtnMouseOver():void {
if (hasPermissionToMute()) {
_rolledOverMuteBtn = true;
}
updateButtons();
}
private function onMuteBtnMouseOut():void {
if (hasPermissionToMute()) {
_rolledOverMuteBtn = false;
}
updateButtons();
}
private function onCanvasMouseOver():void {
_rolledOverCanvas = true;
updateButtons();
}
private function onCanvasMouseOut():void {
_rolledOverCanvas = false;
updateButtons();
}
private function updateButtons():void {
if (user != null) {
if (_rolledOverMuteBtn == user.voiceMuted) {
muteBtn.styleName = "muteOverlayBtn";
} else {
muteBtn.styleName = "unmuteOverlayBtn";
}
if (_rolledOverCanvas || _rolledOverMuteBtn) {
// muteBtnWrapper.visible = user.voiceJoined;
setMuteBtnVisibility(user.voiceJoined);
} else {
// muteBtnWrapper.visible = user.voiceJoined && user.voiceMuted;
setMuteBtnVisibility(user.voiceJoined && user.voiceMuted);
}
var userIconVisibility:Boolean;
if (user.presenter) {
userIcon.source = _images.presenter_white;
userIconVisibility = true;
} else {
if (user.role == BBBUser.MODERATOR) {
userIcon.source = _images.moderator_white;
userIconVisibility = true;
} else {
userIconVisibility = false;
}
}
userIconWrapper.visible = userIconWrapper.includeInLayout = userIconVisibility;
}
}
private function handleUserTalkingEvent(event:CoreEvent):void {
if (user && event.message.userID == user.userID) {
updateButtons();
if (event.message.talking) {
titleBox.setStyle("styleName", "videoToolbarBackgroundTalkingStyle");
} else {
titleBox.setStyle("styleName", "videoToolbarBackgroundNotTalkingStyle");
}
}
}
private function handlePresenterChangedEvent(event:PresenterStatusEvent):void {
if (user && event.userID == user.userID) {
updateButtons();
}
}
private function handleUserVoiceChangedEvent(event:BBBEvent):void {
if (user && event.payload.userID == user.userID) {
updateButtons();
}
}
private function onHideMuteBtnTimerComplete(event:TimerEvent):void {
muteBtnWrapper.visible = false;
}
private function setMuteBtnVisibility(visible:Boolean):void {
if (visible) {
showMuteBtn();
} else {
hideMuteBtn();
}
}
private function hideMuteBtn():void {
_hideMuteBtnTimer.reset();
_hideMuteBtnTimer.start();
}
private function showMuteBtn():void {
_hideMuteBtnTimer.reset();
muteBtnWrapper.visible = true;
}
]]>
</mx:Script>
<mx:Fade id="fadeOut" duration="200" alphaFrom="1.0" alphaTo="0.0" />
<mx:Fade id="fadeIn" duration="200" alphaFrom="0.0" alphaTo="1.0" />
<mx:Canvas id="canvas" width="100%" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off" >
<views:UserAvatar id="avatar" width="100%" height="100%" visible="false" includeInLayout="false" />
<views:UserVideo id="video" width="100%" height="100%" visible="false" includeInLayout="false" />
<mx:VBox id="overlay" width="100%" height="100%" >
<mx:HBox
id="titleBox"
width="100%"
verticalAlign="middle"
paddingRight="0"
paddingLeft="0"
styleName="videoToolbarBackgroundNotTalkingStyle" >
<mx:Box id="userIconWrapper" visible="false" includeInLayout="false" paddingLeft="3" paddingRight="0" >
<mx:Image id="userIcon" />
</mx:Box>
<mx:Label
text="{_username + (_me? ' (' + ResourceUtil.getInstance().getString('bbb.users.usersGrid.nameItemRenderer.youIdentifier') + ')' : '')}"
fontWeight="{_me ? 'bold' : 'normal'}"
width="100%"
paddingLeft="0"
minWidth="0"
truncateToFit="true"
styleName="videoToolbarLabelStyle" />
<mx:Box paddingRight="5">
<mx:Button styleName="closeBtnFocus" buttonMode="true" click="shutdown()" />
</mx:Box>
</mx:HBox>
<mx:Box
width="100%"
paddingTop="15"
paddingRight="15"
horizontalAlign="right" >
<mx:Box
id="muteBtnWrapper"
visible="false"
hideEffect="{fadeOut}" showEffect="{fadeIn}" >
<mx:Button
id="muteBtn"
styleName="talkingOverlayBtn"
buttonMode="true"
click="onMuteBtnClick(event)"
mouseOver="onMuteBtnMouseOver()"
mouseOut="onMuteBtnMouseOut()" />
</mx:Box>
</mx:Box>
</mx:VBox>
</mx:Canvas>
</mx:VBox>

View File

@ -0,0 +1,207 @@
package org.bigbluebutton.modules.videoconf.views
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.AsyncErrorEvent;
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.filters.ConvolutionFilter;
import flash.text.TextField;
import flash.media.Camera;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import mx.utils.ObjectUtil;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.views.VideoWithWarnings;
import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
public class UserVideo extends UserGraphic {
protected var _camIndex:int = -1;
protected var _ns:NetStream;
protected var _shuttingDown:Boolean = false;
protected var _streamName:String;
protected var _video:VideoWithWarnings = null;
protected var _videoProfile:VideoProfile;
protected var _dispatcher:Dispatcher = new Dispatcher();
public function UserVideo() {
super();
_video = new VideoWithWarnings();
_background.addChild(_video);
}
public function publish(camIndex:int, videoProfile:VideoProfile):void {
_camIndex = camIndex;
_videoProfile = videoProfile;
setOriginalDimensions(_videoProfile.width, _videoProfile.height);
_video.updateCamera(camIndex, _videoProfile, _background.width, _background.height);
invalidateDisplayList();
startPublishing();
}
private function newStreamName():String {
/**
* Add timestamp to create a unique stream name. This way we can record
* stream without overwriting previously recorded streams.
*/
var d:Date = new Date();
var curTime:Number = d.getTime();
var uid:String = user.userID;
return _videoProfile.id + "-" + uid + "-" + curTime;
}
protected function getVideoProfile(stream:String):VideoProfile {
trace("Parsing stream name [" + stream + "]");
var pattern:RegExp = new RegExp("([A-Za-z0-9]+)-([A-Za-z0-9]+)-\\d+", "");
if (pattern.test(stream)) {
trace("The stream name is well formatted");
trace("Video profile resolution is [" + pattern.exec(stream)[1] + "]");
trace("Userid [" + pattern.exec(stream)[2] + "]");
return BBB.getVideoProfileById(pattern.exec(stream)[1]);
} else {
trace("Bad stream name format");
var profile:VideoProfile = BBB.defaultVideoProfile;
if (profile == null) {
profile = BBB.fallbackVideoProfile;
}
return profile;
}
}
private function startPublishing():void {
_streamName = newStreamName();
_shuttingDown = false;
var e:StartBroadcastEvent = new StartBroadcastEvent();
e.stream = _streamName;
e.camera = _video.getCamera();
e.videoProfile = _videoProfile;
_dispatcher.dispatchEvent(e);
}
public function shutdown():void {
if (!_shuttingDown) {
_shuttingDown = true;
if (_ns) {
stopViewing();
_ns.close();
_ns = null;
}
if (_video.cameraState()) {
stopPublishing();
}
if (_video) {
_video.disableCamera();
}
}
}
private function stopViewing():void {
var stopEvent:StoppedViewingWebcamEvent = new StoppedViewingWebcamEvent();
stopEvent.webcamUserID = user.userID;
stopEvent.streamName = _streamName;
_dispatcher.dispatchEvent(stopEvent);
user.removeViewingStream(_streamName);
}
private function stopPublishing():void {
var e:StopBroadcastEvent = new StopBroadcastEvent();
e.stream = _streamName;
e.camId = _camIndex;
_dispatcher.dispatchEvent(e);
}
public function view(connection:NetConnection, streamName:String):void {
_streamName = streamName;
_shuttingDown = false;
_ns = new NetStream(connection);
_ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
_ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
_ns.client = this;
_ns.bufferTime = 0;
_ns.receiveVideo(true);
_ns.receiveAudio(false);
_videoProfile = getVideoProfile(streamName);
trace("Remote video profile: " + _videoProfile.toString());
if (_videoProfile == null) {
throw("Invalid video profile");
return;
}
setOriginalDimensions(_videoProfile.width, _videoProfile.height);
_video.attachNetStream(_ns, _videoProfile, _background.width, _background.height);
if (options.applyConvolutionFilter) {
var filter:ConvolutionFilter = new ConvolutionFilter();
filter.matrixX = 3;
filter.matrixY = 3;
trace("Applying convolution filter =[" + options.convolutionFilter + "]");
filter.matrix = options.convolutionFilter;
filter.bias = options.filterBias;
filter.divisor = options.filterDivisor;
_video.videoFilters([filter]);
}
_ns.play(streamName);
user.addViewingStream(streamName);
invalidateDisplayList();
}
private function onNetStatus(e:NetStatusEvent):void{
switch(e.info.code){
case "NetStream.Publish.Start":
trace("NetStream.Publish.Start for broadcast stream " + _streamName);
break;
case "NetStream.Play.UnpublishNotify":
shutdown();
break;
case "NetStream.Play.Start":
trace("Netstatus: " + e.info.code);
_dispatcher.dispatchEvent(new BBBEvent(BBBEvent.VIDEO_STARTED));
break;
case "NetStream.Play.FileStructureInvalid":
trace("The MP4's file structure is invalid.");
break;
case "NetStream.Play.NoSupportedTrackFound":
trace("The MP4 doesn't contain any supported tracks");
break;
}
}
private function onAsyncError(e:AsyncErrorEvent):void{
trace(ObjectUtil.toString(e));
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
updateDisplayListHelper(unscaledWidth, unscaledHeight, _video);
}
public function get camIndex():int {
return _camIndex;
}
public function get streamName():String {
return _streamName;
}
}
}

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<MDIWindow xmlns="flexlib.mdi.containers.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="init()"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
creationComplete="onCreationComplete()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
title="{ResourceUtil.getInstance().getString('bbb.videodock.title')}"
xmlns:mate="http://mate.asfusion.com/" styleNameFocus="videoDockStyleFocus"
layout="absolute" visible="true" styleNameNoFocus="videoDockStyleNoFocus"
horizontalAlign="center"
verticalAlign="middle" >
<mx:Script>
<![CDATA[
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.util.i18n.ResourceUtil;
private function init():void {
}
private function onCreationComplete():void {
this.showCloseButton = false;
}
public function getPrefferedPosition():String {
return MainCanvas.BOTTOM_LEFT;
}
]]>
</mx:Script>
</MDIWindow>

View File

@ -1,278 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 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 3.0 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/>.
-->
<viewVid:VideoWindowItf
xmlns:viewVid="org.bigbluebutton.modules.videoconf.business.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="onCreationComplete()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
styleNameFocus="videoViewStyleFocus"
styleNameNoFocus="videoViewStyleNoFocus"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
resize="onResize()"
layout="absolute">
<mate:Listener type="{BBBEvent.USER_VOICE_MUTED}" method="handleUserVoiceMutedEvent" />
<mate:Listener type="{EventConstants.USER_TALKING}" method="handleUserTalkingEvent" />
<mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" />
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="handleMadePresenterEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_JOINED}" method="handleNewRoleEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_LEFT}" method="handleNewRoleEvent" />
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.Role;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.business.TalkingButtonOverlay;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
private var ns:NetStream;
private var globalDispatcher:Dispatcher;
[Bindable]
public var videoOptions:VideoConfOptions = new VideoConfOptions();
private var windowType:String = "VideoWindowType";
[Bindable] public var glowColor:String = "";
[Bindable] public var glowBlurSize:Number = 0;
override public function getWindowType():String {
return windowType;
}
private function onCreationComplete():void{
this.glowColor = videoOptions.glowColor;
this.glowBlurSize = videoOptions.glowBlurSize;
LogUtil.debug("checking glow values: "+this.glowColor+" and "+this.glowBlurSize);
_videoHolder = new UIComponent();
_videoHolder.addChild(_video);
this.addChild(_videoHolder);
addEventListener(MDIWindowEvent.RESIZE_START, onResizeStart);
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEnd);
addEventListener(MDIWindowEvent.CLOSE, onCloseEvent);
addEventListener(MouseEvent.MOUSE_OVER, showButtons);
addEventListener(MouseEvent.MOUSE_OUT, hideButtons);
addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
createButtons();
addControlButtons();
globalDispatcher = new Dispatcher();
this.minWidth = _minWidth;
this.minHeight = _minHeight;
maximizeRestoreBtn.visible = false;
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();
this.showCloseButton = videoOptions.showCloseButton;
}
private function handleMadePresenterEvent(event:MadePresenterEvent):void {
trace("******** VideoWindow: HandleMadePresenter event *********");
updateControlButtons();
}
private function handleSwitchedPresenterEvent(event:SwitchedPresenterEvent):void {
trace("******** VideoWindow: handleSwitchedPresenterEvent event *********");
updateControlButtons();
}
private function handleNewRoleEvent(event:Event):void {
updateControlButtons();
}
private function handleUserVoiceMutedEvent(event:BBBEvent):void {
if (event.payload.userID == userID) {
userMuted(event.payload.muted);
}
}
private var _closing:Boolean = false;
private function onCloseEvent(event:MDIWindowEvent = null):void {
LogUtil.debug("ViewWindow closing " + streamName);
if (!_closing) {
_closing = true;
var stopEvent:StoppedViewingWebcamEvent = new StoppedViewingWebcamEvent();
stopEvent.webcamUserID = userID;
globalDispatcher.dispatchEvent(stopEvent);
if (UserManager.getInstance().getConference().hasUser(userID)) {
UserManager.getInstance().getConference().getUser(userID).viewingStream = false;
}
}
}
private function handleUserTalkingEvent(event:CoreEvent):void {
if (event.message.userID == userID) {
if (event.message.talking) {
notTalkingEffect.end();
talkingEffect.play([this]);
simulateClick();
} else {
talkingEffect.end();
notTalkingEffect.play([this]);
}
}
}
public function startVideo(connection:NetConnection, stream:String):void{
ns = new NetStream(connection);
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
ns.client = this;
ns.bufferTime = 0;
ns.receiveVideo(true);
ns.receiveAudio(false);
var res:Array = getVideoResolution(stream);
if (res == null) // error
return;
_video = new Video(Number(res[0]), Number(res[1]));
_video.width = Number(res[0]);
_video.height = Number(res[1]);
_video.smoothing = true;
setAspectRatio(Number(res[0]), Number(res[1]));
_video.attachNetStream(ns);
if (videoOptions.smoothVideo) {
trace("Smoothing video.")
_video.smoothing = true;
}
if (videoOptions.applyConvolutionFilter) {
var filter:ConvolutionFilter = new flash.filters.ConvolutionFilter();
filter.matrixX = 3;
filter.matrixY = 3;
trace("Applying convolution filter =[" + videoOptions.convolutionFilter + "]");
filter.matrix = videoOptions.convolutionFilter;
filter.bias = videoOptions.filterBias;
filter.divisor = videoOptions.filterDivisor;
_video.filters = [filter];
}
ns.play(stream);
this.streamName = stream;
this.width = _video.width + paddingHorizontal;
this.height = _video.height + paddingVertical;
if (UserManager.getInstance().getConference().hasUser(userID)) {
UserManager.getInstance().getConference().getUser(userID).viewingStream = true;
}
}
private function onAsyncError(e:AsyncErrorEvent):void{
LogUtil.debug("VideoWindow::asyncerror " + e.toString());
}
public function onMetaData(info:Object):void{
trace("metadata: width=" + info.width + " height=" + info.height);
_video.width = info.width;
_video.height = info.height;
setAspectRatio(info.width, info.height);
onResize();
}
private function onNetStatus(e:NetStatusEvent):void{
switch(e.info.code){
case "NetStream.Publish.Start":
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);
globalDispatcher.dispatchEvent(new BBBEvent(BBBEvent.VIDEO_STARTED));
break;
case "NetStream.Play.FileStructureInvalid":
LogUtil.debug("The MP4's file structure is invalid.");
break;
case "NetStream.Play.NoSupportedTrackFound":
LogUtil.debug("The MP4 doesn't contain any supported tracks");
break;
}
}
override public function close(event:MouseEvent=null):void{
ns.close();
onCloseEvent();
super.close(event);
}
private function closeWindow(e:CloseAllWindowsEvent):void{
this.close();
}
]]>
</mx:Script>
<mx:Glow id="talkingEffect" duration="500" alphaFrom="1.0" alphaTo="0.3"
blurXFrom="0.0" blurXTo="{glowBlurSize}" blurYFrom="0.0" blurYTo="{glowBlurSize}" color="{glowColor}"/>
<mx:Glow id="notTalkingEffect" duration="500" alphaFrom="0.3" alphaTo="1.0"
blurXFrom="{glowBlurSize}" blurXTo="0.0" blurYFrom="{glowBlurSize}" blurYTo="0.0" color="{glowColor}"/>
</viewVid:VideoWindowItf>

View File

@ -50,6 +50,7 @@ sudo mkdir -p /var/bigbluebutton/recording/status/processed/
sudo mkdir -p /var/bigbluebutton/recording/status/sanity/
sudo mv /usr/local/bigbluebutton/core/scripts/*.nginx /etc/bigbluebutton/nginx/
sudo service nginx reload
sudo chown -R tomcat7:tomcat7 /var/bigbluebutton/ /var/log/bigbluebutton/
sudo chown -R red5:red5 /var/bigbluebutton/deskshare/
sudo chown -R freeswitch:daemon /var/bigbluebutton/meetings/