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:
commit
9a7bae79a8
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -127,7 +127,8 @@ case class UserShareWebcam(
|
||||
|
||||
case class UserUnshareWebcam(
|
||||
meetingID: String,
|
||||
userId: String
|
||||
userId: String,
|
||||
stream: String
|
||||
) extends InMessage
|
||||
|
||||
case class ChangeUserStatus(
|
||||
|
@ -67,7 +67,7 @@ case class UserVO(
|
||||
presenter: Boolean,
|
||||
hasStream: Boolean,
|
||||
locked: Boolean,
|
||||
webcamStream: String,
|
||||
webcamStreams: Set[String],
|
||||
phoneUser: Boolean,
|
||||
voiceUser: VoiceUser,
|
||||
listenOnly: Boolean,
|
||||
|
860
bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala
Executable file → Normal file
860
bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala
Executable file → Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
Binary file not shown.
After Width: | Height: | Size: 697 B |
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
73
bigbluebutton-client/resources/prod/profiles.xml
Normal file
73
bigbluebutton-client/resources/prod/profiles.xml
Normal 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>
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 |
@ -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();
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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>
|
File diff suppressed because it is too large
Load Diff
@ -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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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);
|
||||
|
||||
|
@ -123,7 +123,8 @@ package org.bigbluebutton.modules.users.services
|
||||
},
|
||||
function(status:String):void { // status - On error occurred
|
||||
LogUtil.error(status);
|
||||
}
|
||||
},
|
||||
streamName
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
997
bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as
Executable file → Normal file
997
bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as
Executable file → Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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/
|
||||
|
Loading…
Reference in New Issue
Block a user