Merge pull request #14471 from pedrobmarin/r-w-ach

refactor(webcam): assemble core handlers
This commit is contained in:
Paulo Lanzarin 2022-03-04 12:31:42 -03:00 committed by GitHub
commit 21d93f3d67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 755 additions and 744 deletions

View File

@ -3,7 +3,7 @@ package org.bigbluebutton
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, MuteUserInVoiceConfSysMsg, MuteUserInVoiceConfSysMsgBody, Routing }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.{ MeetingStatus2x }
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.apps.webcam.CameraHdlrHelpers
import org.bigbluebutton.core.models.{
Roles,
Users2x,
@ -75,15 +75,6 @@ object LockSettingsUtil {
}
}
private def requestBroadcastedCamEjection(
meetingId: String, userId: String, streamId: String, outGW: OutMsgRouter
): Unit = {
val event = MsgBuilder.buildCamBroadcastStopSysMsg(
meetingId, userId, streamId
)
outGW.send(event)
}
def isCameraBroadcastLocked(user: UserState, liveMeeting: LiveMeeting): Boolean = {
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
@ -94,10 +85,9 @@ object LockSettingsUtil {
user: UserState, stream: WebcamStream, liveMeeting: LiveMeeting
): Boolean = {
var locked = false
val publisherUserId: String = stream.stream.userId
for {
publisher <- Users2x.findWithIntId(liveMeeting.users2x, publisherUserId)
publisher <- Users2x.findWithIntId(liveMeeting.users2x, stream.userId)
} yield {
if (MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status)
&& publisher.role != Roles.MODERATOR_ROLE
@ -110,29 +100,18 @@ object LockSettingsUtil {
locked
}
private def requestCamSubscriptionEjection(
meetingId: String, userId: String, streamId: String, outGW: OutMsgRouter
): Unit = {
val event = MsgBuilder.buildCamStreamUnsubscribeSysMsg(
meetingId, userId, streamId
)
outGW.send(event)
}
private def enforceSeeOtherViewersForUser(
user: UserState, liveMeeting: LiveMeeting, outGW: OutMsgRouter
): Unit = {
if (MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status)) {
Webcams.findAll(liveMeeting.webcams) foreach { webcam =>
val streamId = webcam.stream.id
val userId = user.intId
if (isCameraSubscribeLocked(user, webcam, liveMeeting)
&& Webcams.isViewingWebcam(liveMeeting.webcams, user.intId, webcam.stream.id)) {
requestCamSubscriptionEjection(
&& Webcams.isSubscriber(liveMeeting.webcams, user.intId, webcam.streamId)) {
CameraHdlrHelpers.requestCamSubscriptionEjection(
liveMeeting.props.meetingProp.intId,
userId,
streamId,
user.intId,
webcam.streamId,
outGW
)
}
@ -146,10 +125,10 @@ object LockSettingsUtil {
if (isCameraBroadcastLocked(user, liveMeeting)) {
val broadcastedWebcams = Webcams.findWebcamsForUser(liveMeeting.webcams, user.intId)
broadcastedWebcams foreach { webcam =>
requestBroadcastedCamEjection(
CameraHdlrHelpers.requestBroadcastedCamEjection(
liveMeeting.props.meetingProp.intId,
user.intId,
webcam.stream.id,
webcam.streamId,
outGW
)
}

View File

@ -1,29 +0,0 @@
package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait GetCamBroadcastPermissionReqMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleGetCamBroadcastPermissionReqMsg(msg: GetCamBroadcastPermissionReqMsg) {
val allowed = CameraHdlrHelpers.isCameraBroadcastAllowed(
liveMeeting,
msg.body.meetingId,
msg.body.userId,
msg.body.streamId
)
val event = MsgBuilder.buildGetCamBroadcastPermissionRespMsg(
liveMeeting.props.meetingProp.intId,
msg.body.userId,
msg.body.streamId,
msg.body.sfuSessionId,
allowed
)
outGW.send(event)
}
}

View File

@ -1,36 +0,0 @@
package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.models.{ Webcams }
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait GetCamSubscribePermissionReqMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleGetCamSubscribePermissionReqMsg(msg: GetCamSubscribePermissionReqMsg) {
var allowed = false
for {
stream <- Webcams.findWithStreamId(liveMeeting.webcams, msg.body.streamId)
} yield {
allowed = CameraHdlrHelpers.isCameraSubscribeAllowed(
liveMeeting,
msg.body.meetingId,
msg.body.userId,
stream
)
}
val event = MsgBuilder.buildGetCamSubscribePermissionRespMsg(
liveMeeting.props.meetingProp.intId,
msg.body.userId,
msg.body.streamId,
msg.body.sfuSessionId,
allowed
)
outGW.send(event)
}
}

View File

@ -12,6 +12,5 @@ trait GetUsersMeetingReqMsgHdlr extends HandlerHelpers {
def handleGetUsersMeetingReqMsg(msg: GetUsersMeetingReqMsg): Unit = {
sendAllUsersInMeeting(msg.body.userId)
sendAllVoiceUsersInMeeting(msg.body.userId, liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId)
sendAllWebcamStreams(outGW, msg.body.userId, liveMeeting.webcams, liveMeeting.props.meetingProp.intId)
}
}

View File

@ -1,29 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
trait GetWebcamsOnlyForModeratorReqMsgHdlr {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleGetWebcamsOnlyForModeratorReqMsg(msg: GetWebcamsOnlyForModeratorReqMsg) {
def buildGetWebcamsOnlyForModeratorRespMsg(meetingId: String, userId: String, webcamsOnlyForModerator: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetWebcamsOnlyForModeratorRespMsg.NAME, routing)
val body = GetWebcamsOnlyForModeratorRespMsgBody(webcamsOnlyForModerator, userId)
val header = BbbClientMsgHeader(GetWebcamsOnlyForModeratorRespMsg.NAME, meetingId, userId)
val event = GetWebcamsOnlyForModeratorRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildGetWebcamsOnlyForModeratorRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy,
MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status))
outGW.send(event)
}
}

View File

@ -1,41 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.LockSettingsUtil
trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleUpdateWebcamsOnlyForModeratorCmdMsg(msg: UpdateWebcamsOnlyForModeratorCmdMsg) {
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId) || liveMeeting.props.meetingProp.isBreakout) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to change lock settings"
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
} else {
if (MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status) != msg.body.webcamsOnlyForModerator) {
log.info("Change webcams only for moderator status. meetingId=" + liveMeeting.props.meetingProp.intId + " webcamsOnlyForModeratorrecording=" + msg.body.webcamsOnlyForModerator)
MeetingStatus2x.setWebcamsOnlyForModerator(liveMeeting.status, msg.body.webcamsOnlyForModerator)
LockSettingsUtil.enforceCamLockSettingsForAllUsers(liveMeeting, outGW)
val event = buildWebcamsOnlyForModeratorChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.webcamsOnlyForModerator)
outGW.send(event)
}
}
def buildWebcamsOnlyForModeratorChangedEvtMsg(meetingId: String, userId: String, webcamsOnlyForModerator: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(WebcamsOnlyForModeratorChangedEvtMsg.NAME, routing)
val body = WebcamsOnlyForModeratorChangedEvtMsgBody(webcamsOnlyForModerator, userId)
val header = BbbClientMsgHeader(WebcamsOnlyForModeratorChangedEvtMsg.NAME, meetingId, userId)
val event = WebcamsOnlyForModeratorChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}
}

View File

@ -1,47 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core2.message.handlers.{ CameraHdlrHelpers }
import org.bigbluebutton.core.models.{ MediaStream, WebcamStream, Webcams }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.PermissionCheck
trait UserBroadcastCamStartMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleUserBroadcastCamStartMsg(msg: UserBroadcastCamStartMsg): Unit = {
def broadcastEvent(msg: UserBroadcastCamStartMsg): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(UserBroadcastCamStartedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UserBroadcastCamStartedEvtMsg.NAME, props.meetingProp.intId, msg.header.userId)
val body = UserBroadcastCamStartedEvtMsgBody(msg.header.userId, msg.body.stream)
val event = UserBroadcastCamStartedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
val allowed = CameraHdlrHelpers.isCameraBroadcastAllowed(
liveMeeting,
msg.header.meetingId,
msg.header.userId,
msg.body.stream
)
if (!allowed) {
val reason = "No permission to share camera."
PermissionCheck.ejectUserForFailedPermission(props.meetingProp.intId, msg.header.userId, reason, outGW, liveMeeting)
} else {
val stream = new MediaStream(msg.body.stream, msg.body.stream, msg.header.userId, Map.empty, Set.empty)
val webcamStream = new WebcamStream(msg.body.stream, stream)
for {
uvo <- Webcams.addWebcamBroadcastStream(liveMeeting.webcams, webcamStream)
} yield {
broadcastEvent(msg)
}
}
}
}

View File

@ -1,36 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserBroadcastCamStopMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleUserBroadcastCamStopMsg(msg: UserBroadcastCamStopMsg): Unit = {
for {
publisherStream <- Webcams.findWithStreamId(liveMeeting.webcams, msg.body.stream)
} yield {
if (publisherStream.stream.userId != msg.header.userId
|| !msg.body.stream.startsWith(msg.header.userId)) {
val reason = "User does not own camera stream"
PermissionCheck.ejectUserForFailedPermission(
props.meetingProp.intId, msg.header.userId, reason, outGW, liveMeeting
)
} else {
for {
_ <- Webcams.removeWebcamBroadcastStream(liveMeeting.webcams, msg.body.stream)
} yield {
val event = MsgBuilder.buildUserBroadcastCamStoppedEvtMsg(
props.meetingProp.intId, msg.header.userId, msg.body.stream
)
outGW.send(event)
}
}
}
}
}

View File

@ -165,16 +165,13 @@ class UsersApp(
with SetRecordingStatusCmdMsgHdlr
with RecordAndClearPreviousMarkersCmdMsgHdlr
with SendRecordingTimerInternalMsgHdlr
with UpdateWebcamsOnlyForModeratorCmdMsgHdlr
with GetRecordingStatusReqMsgHdlr
with SelectRandomViewerReqMsgHdlr
with GetWebcamsOnlyForModeratorReqMsgHdlr
with AssignPresenterReqMsgHdlr
with ChangeUserPinStateReqMsgHdlr
with EjectDuplicateUserReqMsgHdlr
with EjectUserFromMeetingCmdMsgHdlr
with EjectUserFromMeetingSysMsgHdlr
with SyncGetWebcamInfoRespMsgHdlr
with MuteUserCmdMsgHdlr {
val log = Logging(context.system, getClass)

View File

@ -0,0 +1,29 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.LiveMeeting
trait CamBroadcastStoppedInSfuEvtMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: CamBroadcastStoppedInSfuEvtMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId
val userId = msg.header.userId
val streamId = msg.body.streamId
CameraHdlrHelpers.stopBroadcastedCam(
liveMeeting,
meetingId,
userId,
streamId,
bus.outGW
)
}
}

View File

@ -0,0 +1,36 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.LiveMeeting
trait CamStreamSubscribedInSfuEvtMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: CamStreamSubscribedInSfuEvtMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
) {
val meetingId = liveMeeting.props.meetingProp.intId
val allowed = CameraHdlrHelpers.isCameraSubscribeAllowed(
liveMeeting,
meetingId,
msg.header.userId,
msg.body.streamId
)
if (allowed) {
Webcams.addSubscriber(liveMeeting.webcams, msg.body.streamId, msg.header.userId)
} else {
CameraHdlrHelpers.requestCamSubscriptionEjection(
meetingId,
msg.header.userId,
msg.body.streamId,
bus.outGW
)
}
}
}

View File

@ -0,0 +1,23 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.LiveMeeting
trait CamStreamUnsubscribedInSfuEvtMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: CamStreamUnsubscribedInSfuEvtMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
) {
Webcams.removeSubscriber(
liveMeeting.webcams,
msg.body.streamId,
msg.header.userId
)
}
}

View File

@ -0,0 +1,194 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.models.{ Users2x, Webcams, WebcamStream }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
object CameraHdlrHelpers extends SystemConfiguration with RightsManagementTrait {
def isCameraBroadcastAllowed(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
streamId: String
): Boolean = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(user) => {
val camBroadcastLocked = LockSettingsUtil.isCameraBroadcastLocked(user, liveMeeting)
val camCapReached = hasReachedCameraCap(liveMeeting, userId)
(applyPermissionCheck &&
!camBroadcastLocked &&
!camCapReached &&
!user.userLeftFlag.left &&
streamId.startsWith(user.intId) &&
liveMeeting.props.meetingProp.intId == meetingId)
}
case _ => false
}
}
def isCameraSubscribeAllowed(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
stream: WebcamStream
): Boolean = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(user) => {
val camSubscribeLocked = LockSettingsUtil.isCameraSubscribeLocked(user, stream, liveMeeting)
(applyPermissionCheck &&
!camSubscribeLocked &&
!user.userLeftFlag.left &&
liveMeeting.props.meetingProp.intId == meetingId)
}
case _ => false
}
}
def isCameraSubscribeAllowed(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
streamId: String
): Boolean = {
Webcams.findWithStreamId(liveMeeting.webcams, streamId) match {
case Some(stream) => isCameraSubscribeAllowed(liveMeeting, meetingId, userId, stream)
case _ => false
}
}
def isCameraEjectAllowed(
liveMeeting: LiveMeeting,
userId: String
): Boolean = {
val allowModsToEjectCameras = liveMeeting.props.usersProp.allowModsToEjectCameras
val isBreakout = liveMeeting.props.meetingProp.isBreakout
val hasPermission = !permissionFailed(
PermissionCheck.MOD_LEVEL,
PermissionCheck.VIEWER_LEVEL,
liveMeeting.users2x,
userId
)
(allowModsToEjectCameras &&
!isBreakout &&
hasPermission)
}
def isWebcamsOnlyForModeratorUpdateAllowed(
liveMeeting: LiveMeeting,
userId: String
): Boolean = {
val isBreakout = liveMeeting.props.meetingProp.isBreakout
val hasPermission = !permissionFailed(
PermissionCheck.MOD_LEVEL,
PermissionCheck.VIEWER_LEVEL,
liveMeeting.users2x,
userId
)
(!isBreakout && hasPermission)
}
def updateWebcamsOnlyForModerator(
liveMeeting: LiveMeeting,
value: Boolean,
outGW: OutMsgRouter
): Option[Boolean] = {
MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status) match {
case x if x != value => {
MeetingStatus2x.setWebcamsOnlyForModerator(liveMeeting.status, value)
LockSettingsUtil.enforceCamLockSettingsForAllUsers(liveMeeting, outGW)
Some(value)
}
case _ => None
}
}
def hasReachedCameraCap(
liveMeeting: LiveMeeting,
userId: String
): Boolean = {
val cameras = Webcams.findWebcamsForUser(liveMeeting.webcams, userId).length
liveMeeting.props.usersProp.userCameraCap match {
case 0 => false // disabled
case x if x > cameras => false
case _ => true
}
}
def stopBroadcastedCam(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
streamId: String,
outGW: OutMsgRouter
): Unit = {
def broadcastEvent(meetingId: String, userId: String, streamId: String) {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(UserBroadcastCamStoppedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UserBroadcastCamStoppedEvtMsg.NAME, meetingId, userId)
val body = UserBroadcastCamStoppedEvtMsgBody(userId, streamId)
val event = UserBroadcastCamStoppedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
if (Webcams.hasWebcamStream(liveMeeting.webcams, streamId)) {
if (Webcams.isPublisher(liveMeeting.webcams, userId, streamId)) {
for {
_ <- Webcams.removeWebcamStream(liveMeeting.webcams, streamId)
} yield broadcastEvent(meetingId, userId, streamId)
} else {
val reason = "User does not own camera stream"
PermissionCheck.ejectUserForFailedPermission(
meetingId,
userId,
reason,
outGW,
liveMeeting
)
}
}
}
def requestBroadcastedCamEjection(
meetingId: String,
userId: String,
streamId: String,
outGW: OutMsgRouter
): Unit = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(CamBroadcastStopSysMsg.NAME, routing)
val body = CamBroadcastStopSysMsgBody(meetingId, userId, streamId)
val header = BbbCoreBaseHeader(CamBroadcastStopSysMsg.NAME)
val event = CamBroadcastStopSysMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
def requestCamSubscriptionEjection(
meetingId: String,
userId: String,
streamId: String,
outGW: OutMsgRouter
): Unit = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(CamStreamUnsubscribeSysMsg.NAME, routing)
val body = CamStreamUnsubscribeSysMsgBody(meetingId, userId, streamId)
val header = BbbCoreBaseHeader(CamStreamUnsubscribeSysMsg.NAME)
val event = CamStreamUnsubscribeSysMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
}

View File

@ -0,0 +1,48 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.LiveMeeting
trait EjectUserCamerasCmdMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: EjectUserCamerasCmdMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId
val requesterUserId = msg.header.userId
val userId = msg.body.userId
val allow = CameraHdlrHelpers.isCameraEjectAllowed(
liveMeeting,
requesterUserId
)
if (!allow) {
val reason = "No permission to eject cameras from user."
PermissionCheck.ejectUserForFailedPermission(
meetingId,
requesterUserId,
reason,
bus.outGW,
liveMeeting
)
} else {
log.info(s"Ejecting user cameras. meetingId=${meetingId} userId=${userId} requesterUserId=${requesterUserId}")
Webcams.findWebcamsForUser(liveMeeting.webcams, userId) foreach { webcam =>
// Goes to SFU and comes back through CamBroadcastStoppedInSfuEvtMsg
CameraHdlrHelpers.requestBroadcastedCamEjection(
meetingId,
userId,
webcam.streamId,
bus.outGW
)
}
}
}
}

View File

@ -0,0 +1,55 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.LiveMeeting
trait GetCamBroadcastPermissionReqMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: GetCamBroadcastPermissionReqMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
) {
val meetingId = liveMeeting.props.meetingProp.intId
def broadcastEvent(
meetingId: String,
userId: String,
streamId: String,
sfuSessionId: String,
allowed: Boolean
) {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetCamBroadcastPermissionRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetCamBroadcastPermissionRespMsg.NAME, meetingId, userId)
val body = GetCamBroadcastPermissionRespMsgBody(
meetingId,
userId,
streamId,
sfuSessionId,
allowed
)
val event = GetCamBroadcastPermissionRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val allowed = CameraHdlrHelpers.isCameraBroadcastAllowed(
liveMeeting,
meetingId,
msg.body.userId,
msg.body.streamId
)
broadcastEvent(
meetingId,
msg.body.userId,
msg.body.streamId,
msg.body.sfuSessionId,
allowed
)
}
}

View File

@ -0,0 +1,55 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.LiveMeeting
trait GetCamSubscribePermissionReqMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: GetCamSubscribePermissionReqMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
) {
val meetingId = liveMeeting.props.meetingProp.intId
def broadcastEvent(
meetingId: String,
userId: String,
streamId: String,
sfuSessionId: String,
allowed: Boolean
) {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetCamSubscribePermissionRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetCamSubscribePermissionRespMsg.NAME, meetingId, userId)
val body = GetCamSubscribePermissionRespMsgBody(
meetingId,
userId,
streamId,
sfuSessionId,
allowed
)
val event = GetCamSubscribePermissionRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val allowed = CameraHdlrHelpers.isCameraSubscribeAllowed(
liveMeeting,
meetingId,
msg.body.userId,
msg.body.streamId
)
broadcastEvent(
meetingId,
msg.body.userId,
msg.body.streamId,
msg.body.sfuSessionId,
allowed
)
}
}

View File

@ -0,0 +1,33 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.MeetingStatus2x
trait GetWebcamsOnlyForModeratorReqMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: GetWebcamsOnlyForModeratorReqMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
) {
val meetingId = liveMeeting.props.meetingProp.intId
def broadcastEvent(meetingId: String, userId: String, webcamsOnlyForModerator: Boolean) {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetWebcamsOnlyForModeratorRespMsg.NAME, routing)
val body = GetWebcamsOnlyForModeratorRespMsgBody(webcamsOnlyForModerator, userId)
val header = BbbClientMsgHeader(GetWebcamsOnlyForModeratorRespMsg.NAME, meetingId, userId)
val event = GetWebcamsOnlyForModeratorRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val webcamsOnlyForModerator = MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status)
broadcastEvent(meetingId, msg.body.requestedBy, webcamsOnlyForModerator)
}
}

View File

@ -1,16 +1,16 @@
package org.bigbluebutton.core.apps.users
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.{
Users2x,
VoiceUsers,
Webcams
}
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.LiveMeeting
trait SyncGetWebcamInfoRespMsgHdlr {
this: UsersApp =>
this: WebcamApp2x =>
def handleSyncGetWebcamInfoRespMsg(liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(
@ -25,22 +25,20 @@ trait SyncGetWebcamInfoRespMsgHdlr {
)
val webcamsList = Webcams.findAll(liveMeeting.webcams) flatMap { webcam =>
val stream = webcam.stream.id
val userId = webcam.stream.userId
val streamId = webcam.streamId
val userId = webcam.userId
val pin = Users2x.isPin(userId, liveMeeting.users2x)
for {
u <- Users2x.findWithIntId(liveMeeting.users2x, userId)
} yield {
VoiceUsers.findWIthIntId(liveMeeting.voiceUsers, userId) match {
case Some(vu) => WebcamStreamInfo(stream, userId, u.name, pin, vu.floor, vu.lastFloorTime)
case _ => WebcamStreamInfo(stream, userId, u.name, pin, false, "0")
case Some(vu) => WebcamStreamInfo(streamId, userId, u.name, pin, vu.floor, vu.lastFloorTime)
case _ => WebcamStreamInfo(streamId, userId, u.name, pin, false, "0")
}
}
}
val body = SyncGetWebcamInfoRespMsgBody(
webcamsList
)
val body = SyncGetWebcamInfoRespMsgBody(webcamsList)
val event = SyncGetWebcamInfoRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)

View File

@ -0,0 +1,57 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.LiveMeeting
trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: UpdateWebcamsOnlyForModeratorCmdMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
) {
val meetingId = liveMeeting.props.meetingProp.intId
def broadcastEvent(meetingId: String, userId: String, webcamsOnlyForModerator: Boolean) {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(WebcamsOnlyForModeratorChangedEvtMsg.NAME, routing)
val body = WebcamsOnlyForModeratorChangedEvtMsgBody(webcamsOnlyForModerator, userId)
val header = BbbClientMsgHeader(WebcamsOnlyForModeratorChangedEvtMsg.NAME, meetingId, userId)
val event = WebcamsOnlyForModeratorChangedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val allow = CameraHdlrHelpers.isWebcamsOnlyForModeratorUpdateAllowed(
liveMeeting,
msg.header.userId
)
if (!allow) {
val reason = "No permission to change lock settings"
PermissionCheck.ejectUserForFailedPermission(
meetingId,
msg.header.userId,
reason,
bus.outGW,
liveMeeting
)
} else {
CameraHdlrHelpers.updateWebcamsOnlyForModerator(
liveMeeting,
msg.body.webcamsOnlyForModerator,
bus.outGW
) match {
case Some(value) => {
log.info(s"Change webcams only for moderator status. meetingId=${meetingId} value=${value}")
broadcastEvent(meetingId, msg.body.setBy, value)
}
case _ =>
}
}
}
}

View File

@ -0,0 +1,54 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.{ WebcamStream, Webcams }
import org.bigbluebutton.core.running.LiveMeeting
trait UserBroadcastCamStartMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: UserBroadcastCamStartMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId
def broadcastEvent(meetingId: String, userId: String, streamId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(UserBroadcastCamStartedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UserBroadcastCamStartedEvtMsg.NAME, meetingId, userId)
val body = UserBroadcastCamStartedEvtMsgBody(userId, streamId)
val event = UserBroadcastCamStartedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val allowed = CameraHdlrHelpers.isCameraBroadcastAllowed(
liveMeeting,
msg.header.meetingId,
msg.header.userId,
msg.body.stream
)
if (!allowed) {
val reason = "No permission to share camera."
PermissionCheck.ejectUserForFailedPermission(
meetingId,
msg.header.userId,
reason,
bus.outGW,
liveMeeting
)
} else {
val webcam = new WebcamStream(msg.body.stream, msg.header.userId, Set.empty)
for {
_ <- Webcams.addWebcamStream(liveMeeting.webcams, webcam)
} yield broadcastEvent(meetingId, msg.header.userId, msg.body.stream)
}
}
}

View File

@ -0,0 +1,29 @@
package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.LiveMeeting
trait UserBroadcastCamStopMsgHdlr {
this: WebcamApp2x =>
def handle(
msg: UserBroadcastCamStopMsg,
liveMeeting: LiveMeeting,
bus: MessageBus
): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId
val userId = msg.header.userId
val streamId = msg.body.stream
CameraHdlrHelpers.stopBroadcastedCam(
liveMeeting,
meetingId,
userId,
streamId,
bus.outGW
)
}
}

View File

@ -0,0 +1,20 @@
package org.bigbluebutton.core.apps.webcam
import akka.actor.ActorContext
import akka.event.Logging
class WebcamApp2x(implicit val context: ActorContext)
extends CamBroadcastStoppedInSfuEvtMsgHdlr
with CamStreamSubscribedInSfuEvtMsgHdlr
with CamStreamUnsubscribedInSfuEvtMsgHdlr
with EjectUserCamerasCmdMsgHdlr
with GetCamBroadcastPermissionReqMsgHdlr
with GetCamSubscribePermissionReqMsgHdlr
with GetWebcamsOnlyForModeratorReqMsgHdlr
with SyncGetWebcamInfoRespMsgHdlr
with UpdateWebcamsOnlyForModeratorCmdMsgHdlr
with UserBroadcastCamStartMsgHdlr
with UserBroadcastCamStopMsgHdlr {
val log = Logging(context.system, getClass)
}

View File

@ -1,22 +0,0 @@
package org.bigbluebutton.core.models
import com.softwaremill.quicklens._
trait Streams {
def addViewer(stream: MediaStream, user: String): MediaStream = {
val newViewers = stream.viewers + user
modify(stream)(_.viewers).setTo(newViewers)
}
def removeViewer(stream: MediaStream, user: String): MediaStream = {
val newViewers = stream.viewers - user
modify(stream)(_.viewers).setTo(newViewers)
}
}
/**
* Borrow some ideas from SDP for attributes.
* https://en.wikipedia.org/wiki/Session_Description_Protocol
*/
case class MediaStream(id: String, url: String, userId: String, attributes: collection.immutable.Map[String, String], viewers: Set[String])

View File

@ -1,97 +1,93 @@
package org.bigbluebutton.core.models
import com.softwaremill.quicklens._
import collection.immutable.HashMap
object Webcams {
def findWithStreamId(webcams: Webcams, streamId: String): Option[WebcamStream] = {
webcams.toVector.find(w => w.stream.id == streamId)
webcams.toVector.find(webcam => webcam.streamId == streamId)
}
def findWebcamsForUser(webcams: Webcams, userId: String): Vector[WebcamStream] = {
webcams.toVector.filter(w => w.stream.userId == userId)
webcams.toVector.filter(webcam => webcam.userId == userId)
}
def findAll(webcams: Webcams): Vector[WebcamStream] = webcams.toVector
def addWebcamBroadcastStream(webcams: Webcams, webcamStream: WebcamStream): Option[WebcamStream] = {
findWithStreamId(webcams, webcamStream.streamId) match {
case Some(p) => {
None
}
case None => {
webcams.save(webcamStream)
Some(webcamStream)
}
def addWebcamStream(webcams: Webcams, webcam: WebcamStream): Option[WebcamStream] = {
findWithStreamId(webcams, webcam.streamId) match {
case None => Some(webcams.save(webcam))
case _ => None
}
}
def removeWebcamBroadcastStream(webcams: Webcams, streamId: String): Option[WebcamStream] = {
def removeWebcamStream(webcams: Webcams, streamId: String): Option[WebcamStream] = {
for {
stream <- findWithStreamId(webcams, streamId)
removedStream <- webcams.remove(streamId)
} yield removedStream
webcam <- webcams.remove(streamId)
} yield webcam
}
def addViewer(webcams: Webcams, streamId: String, subscriberId: String): Unit = {
for {
webcamStream <- findWithStreamId(webcams, streamId)
} yield {
val mediaStream = webcamStream.stream
if (!mediaStream.viewers.contains(subscriberId)) {
val newViewers = mediaStream.viewers + subscriberId
webcams.updateViewers(webcamStream, newViewers)
}
}
}
def removeViewer(webcams: Webcams, streamId: String, subscriberId: String): Unit = {
for {
webcamStream <- findWithStreamId(webcams, streamId)
} yield {
val mediaStream = webcamStream.stream
if (mediaStream.viewers.contains(subscriberId)) {
val newViewers = mediaStream.viewers - subscriberId
webcams.updateViewers(webcamStream, newViewers)
}
}
}
def isViewingWebcam(webcams: Webcams, userId: String, streamId: String): Boolean = {
def hasWebcamStream(webcams: Webcams, streamId: String): Boolean = {
findWithStreamId(webcams, streamId) match {
case Some(webcam) => {
val viewing = webcam.stream.viewers contains userId
viewing
}
case None => {
false
}
case Some(webcam) => true
case _ => false
}
}
def addSubscriber(webcams: Webcams, streamId: String, userId: String): Unit = {
findWithStreamId(webcams, streamId) match {
case Some(webcam) => webcams.addSubscriber(webcam, userId)
case _ =>
}
}
def removeSubscriber(webcams: Webcams, streamId: String, userId: String): Unit = {
findWithStreamId(webcams, streamId) match {
case Some(webcam) => webcams.removeSubscriber(webcam, userId)
case _ =>
}
}
def isSubscriber(webcams: Webcams, userId: String, streamId: String): Boolean = {
findWithStreamId(webcams, streamId) match {
case Some(webcam) => webcam.subscribers contains userId
case None => false
}
}
def isPublisher(webcams: Webcams, userId: String, streamId: String): Boolean = {
findWithStreamId(webcams, streamId) match {
case Some(webcam) => webcam.userId == userId && webcam.streamId.startsWith(userId)
case None => false
}
}
}
class Webcams {
private var webcams: collection.immutable.HashMap[String, WebcamStream] = new collection.immutable.HashMap[String, WebcamStream]
private var webcams: HashMap[String, WebcamStream] = new HashMap[String, WebcamStream]
private def toVector: Vector[WebcamStream] = webcams.values.toVector
private def save(webcam: WebcamStream): WebcamStream = {
webcams += webcam.stream.id -> webcam
webcams += webcam.streamId -> webcam
webcam
}
private def remove(streamId: String): Option[WebcamStream] = {
val webcam = webcams.get(streamId)
webcam foreach (u => webcams -= streamId)
webcam
for {
webcam <- webcams.get(streamId)
} yield {
webcams -= streamId
webcam
}
}
private def updateViewers(webcamStream: WebcamStream, viewers: Set[String]): WebcamStream = {
val mediaStream: MediaStream = webcamStream.stream
val newMediaStream = mediaStream.modify(_.viewers).setTo(viewers)
val newWebcamStream = webcamStream.modify(_.stream).setTo(newMediaStream)
save(newWebcamStream)
private def addSubscriber(webcam: WebcamStream, userId: String): Unit = {
save(webcam.copy(subscribers = webcam.subscribers + userId))
}
private def removeSubscriber(webcam: WebcamStream, userId: String): Unit = {
save(webcam.copy(subscribers = webcam.subscribers - userId))
}
}
case class WebcamStream(streamId: String, stream: MediaStream)
case class WebcamStream(streamId: String, userId: String, subscribers: Set[String])

View File

@ -147,6 +147,10 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[CamBroadcastStoppedInSfuEvtMsg](envelope, jsonNode)
case EjectUserCamerasCmdMsg.NAME =>
routeGenericMsg[EjectUserCamerasCmdMsg](envelope, jsonNode)
case GetWebcamsOnlyForModeratorReqMsg.NAME =>
routeGenericMsg[GetWebcamsOnlyForModeratorReqMsg](envelope, jsonNode)
case UpdateWebcamsOnlyForModeratorCmdMsg.NAME =>
routeGenericMsg[UpdateWebcamsOnlyForModeratorCmdMsg](envelope, jsonNode)
// Pads
case PadCreateGroupReqMsg.NAME =>
@ -338,10 +342,6 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[GetRecordingStatusReqMsg](envelope, jsonNode)
case GetScreenshareStatusReqMsg.NAME =>
routeGenericMsg[GetScreenshareStatusReqMsg](envelope, jsonNode)
case GetWebcamsOnlyForModeratorReqMsg.NAME =>
routeGenericMsg[GetWebcamsOnlyForModeratorReqMsg](envelope, jsonNode)
case UpdateWebcamsOnlyForModeratorCmdMsg.NAME =>
routeGenericMsg[UpdateWebcamsOnlyForModeratorCmdMsg](envelope, jsonNode)
// Lock settings
case LockUserInMeetingCmdMsg.NAME =>

View File

@ -15,19 +15,6 @@ import org.bigbluebutton.core.util.TimeUtil
trait HandlerHelpers extends SystemConfiguration {
def sendAllWebcamStreams(outGW: OutMsgRouter, requesterId: String, webcams: Webcams, meetingId: String): Unit = {
val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams)
val webcamStreams = streams.map { u =>
val msVO = MediaStreamVO(id = u.stream.id, url = u.stream.url, userId = u.stream.userId,
attributes = u.stream.attributes, viewers = u.stream.viewers)
WebcamStreamVO(streamId = msVO.id, stream = msVO)
}
val event = MsgBuilder.buildGetWebcamStreamsMeetingRespMsg(meetingId, requesterId, webcamStreams)
outGW.send(event)
}
def trackUserJoin(
outGW: OutMsgRouter,
liveMeeting: LiveMeeting,

View File

@ -20,6 +20,7 @@ import org.bigbluebutton.core.apps.pads.PadsApp2x
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
import org.bigbluebutton.core.apps.presentation.PresentationApp2x
import org.bigbluebutton.core.apps.users.UsersApp2x
import org.bigbluebutton.core.apps.webcam.WebcamApp2x
import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.models.{ Users2x, VoiceUsers, _ }
@ -71,10 +72,8 @@ class MeetingActor(
with BreakoutApp2x
with UsersApp2x
with UserBroadcastCamStartMsgHdlr
with UserJoinMeetingReqMsgHdlr
with UserJoinMeetingAfterReconnectReqMsgHdlr
with UserBroadcastCamStopMsgHdlr
with UserConnectedToGlobalAudioMsgHdlr
with UserDisconnectedFromGlobalAudioMsgHdlr
with MuteAllExceptPresentersCmdMsgHdlr
@ -83,12 +82,6 @@ class MeetingActor(
with GetGlobalAudioPermissionReqMsgHdlr
with GetScreenBroadcastPermissionReqMsgHdlr
with GetScreenSubscribePermissionReqMsgHdlr
with GetCamBroadcastPermissionReqMsgHdlr
with GetCamSubscribePermissionReqMsgHdlr
with CamStreamSubscribedInSfuEvtMsgHdlr
with CamStreamUnsubscribedInSfuEvtMsgHdlr
with CamBroadcastStoppedInSfuEvtMsgHdlr
with EjectUserCamerasCmdMsgHdlr
with EjectUserFromVoiceCmdMsgHdlr
with EndMeetingSysCmdMsgHdlr
@ -138,6 +131,7 @@ class MeetingActor(
val groupChatApp = new GroupChatHdlrs
val presentationPodsApp = new PresentationPodHdlrs
val pollApp = new PollApp2x
val webcamApp2x = new WebcamApp2x
val wbApp = new WhiteboardApp2x
object ExpiryTrackerHelper extends MeetingExpiryTrackerHelper
@ -376,14 +370,6 @@ class MeetingActor(
case m: UserLeaveReqMsg =>
state = handleUserLeaveReqMsg(m, state)
updateModeratorsPresence()
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
case m: GetCamBroadcastPermissionReqMsg => handleGetCamBroadcastPermissionReqMsg(m)
case m: GetCamSubscribePermissionReqMsg => handleGetCamSubscribePermissionReqMsg(m)
case m: CamStreamSubscribedInSfuEvtMsg => handleCamStreamSubscribedInSfuEvtMsg(m)
case m: CamStreamUnsubscribedInSfuEvtMsg => handleCamStreamUnsubscribedInSfuEvtMsg(m)
case m: CamBroadcastStoppedInSfuEvtMsg => handleCamBroadcastStoppedInSfuEvtMsg(m)
case m: EjectUserCamerasCmdMsg => handleEjectUserCamerasCmdMsg(m)
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
@ -393,8 +379,6 @@ class MeetingActor(
case m: RecordAndClearPreviousMarkersCmdMsg =>
state = usersApp.handleRecordAndClearPreviousMarkersCmdMsg(m, state)
updateUserLastActivity(m.body.setBy)
case m: GetWebcamsOnlyForModeratorReqMsg => usersApp.handleGetWebcamsOnlyForModeratorReqMsg(m)
case m: UpdateWebcamsOnlyForModeratorCmdMsg => usersApp.handleUpdateWebcamsOnlyForModeratorCmdMsg(m)
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
case m: SelectRandomViewerReqMsg => usersApp.handleSelectRandomViewerReqMsg(m)
@ -573,6 +557,18 @@ class MeetingActor(
state = groupChatApp.handle(m, state, liveMeeting, msgBus)
updateUserLastActivity(m.body.msg.sender.id)
// Webcams
case m: UserBroadcastCamStartMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: UserBroadcastCamStopMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: GetCamBroadcastPermissionReqMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: GetCamSubscribePermissionReqMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: CamStreamSubscribedInSfuEvtMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: CamStreamUnsubscribedInSfuEvtMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: CamBroadcastStoppedInSfuEvtMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: EjectUserCamerasCmdMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: GetWebcamsOnlyForModeratorReqMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
case m: UpdateWebcamsOnlyForModeratorCmdMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
// ExternalVideo
case m: StartExternalVideoPubMsg => externalVideoApp2x.handle(m, liveMeeting, msgBus)
case m: UpdateExternalVideoPubMsg => externalVideoApp2x.handle(m, liveMeeting, msgBus)
@ -633,11 +629,11 @@ class MeetingActor(
val liveWebcams: Vector[org.bigbluebutton.core.models.WebcamStream] = findAll(liveMeeting.webcams)
val numOfLiveWebcams: Int = liveWebcams.length
val broadcasts: List[Broadcast] = liveWebcams.map(webcam => Broadcast(
webcam.stream.id,
User(webcam.stream.userId, resolveUserName(webcam.stream.userId)), 0L
webcam.streamId,
User(webcam.userId, resolveUserName(webcam.userId)), 0L
)).toList
val viewers: Set[String] = liveWebcams.flatMap(_.stream.viewers).toSet
val webcamStream: msgs.WebcamStream = msgs.WebcamStream(broadcasts, viewers)
val subscribers: Set[String] = liveWebcams.flatMap(_.subscribers).toSet
val webcamStream: msgs.WebcamStream = msgs.WebcamStream(broadcasts, subscribers)
Webcam(numOfLiveWebcams, webcamStream)
}
@ -700,7 +696,7 @@ class MeetingActor(
screenshareApp2x.handleSyncGetScreenshareInfoRespMsg(liveMeeting, msgBus)
// send all webcam info
usersApp.handleSyncGetWebcamInfoRespMsg(liveMeeting, msgBus)
webcamApp2x.handleSyncGetWebcamInfoRespMsg(liveMeeting, msgBus)
}
def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = {

View File

@ -1,62 +0,0 @@
package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.core.models.{ Users2x, Webcams, WebcamStream }
import org.bigbluebutton.LockSettingsUtil
object CameraHdlrHelpers extends SystemConfiguration {
def isCameraBroadcastAllowed(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
streamId: String
): Boolean = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(user) => {
val camBroadcastLocked = LockSettingsUtil.isCameraBroadcastLocked(user, liveMeeting)
val camCapReached = hasReachedCameraCap(liveMeeting, userId)
(applyPermissionCheck &&
!camBroadcastLocked &&
!camCapReached &&
!user.userLeftFlag.left &&
streamId.startsWith(user.intId) &&
liveMeeting.props.meetingProp.intId == meetingId)
}
case _ => false
}
}
def isCameraSubscribeAllowed(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
stream: WebcamStream
): Boolean = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(user) => {
val camSubscribeLocked = LockSettingsUtil.isCameraSubscribeLocked(user, stream, liveMeeting)
(applyPermissionCheck &&
!camSubscribeLocked &&
!user.userLeftFlag.left &&
liveMeeting.props.meetingProp.intId == meetingId)
}
case _ => false
}
}
def hasReachedCameraCap(
liveMeeting: LiveMeeting,
userId: String
): Boolean = {
val cameras = Webcams.findWebcamsForUser(liveMeeting.webcams, userId).length
liveMeeting.props.usersProp.userCameraCap match {
case 0 => false // disabled
case x if x > cameras => false
case _ => true
}
}
}

View File

@ -1,36 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait CamBroadcastStoppedInSfuEvtMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleCamBroadcastStoppedInSfuEvtMsg(msg: CamBroadcastStoppedInSfuEvtMsg): Unit = {
for {
publisherStream <- Webcams.findWithStreamId(liveMeeting.webcams, msg.body.streamId)
} yield {
if (publisherStream.stream.userId != msg.header.userId
|| !msg.body.streamId.startsWith(msg.header.userId)) {
val reason = "User does not own camera stream"
PermissionCheck.ejectUserForFailedPermission(
props.meetingProp.intId, msg.header.userId, reason, outGW, liveMeeting
)
} else {
for {
_ <- Webcams.removeWebcamBroadcastStream(liveMeeting.webcams, msg.body.streamId)
} yield {
val event = MsgBuilder.buildUserBroadcastCamStoppedEvtMsg(
props.meetingProp.intId, msg.header.userId, msg.body.streamId
)
outGW.send(event)
}
}
}
}
}

View File

@ -1,50 +0,0 @@
package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.models.{ Users2x }
import org.bigbluebutton.core.models.Webcams.{ findWithStreamId, addViewer }
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.LockSettingsUtil
trait CamStreamSubscribedInSfuEvtMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def isAllowedToSubscribeToCam(userId: String, streamId: String): Boolean = {
var allowed = false
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
stream <- findWithStreamId(liveMeeting.webcams, streamId)
} yield {
val camSubscribeLocked = LockSettingsUtil.isCameraSubscribeLocked(user, stream, liveMeeting)
if (!user.userLeftFlag.left
&& (applyPermissionCheck && !camSubscribeLocked)) {
allowed = true
}
}
allowed
}
def handleCamStreamSubscribedInSfuEvtMsg(msg: CamStreamSubscribedInSfuEvtMsg) {
// Subscriber's user ID
val userId = msg.header.userId
// Publisher's stream ID
val streamId = msg.body.streamId
val allowed = isAllowedToSubscribeToCam(userId, streamId)
if (allowed) {
addViewer(liveMeeting.webcams, streamId, userId)
} else {
val event = MsgBuilder.buildCamStreamUnsubscribeSysMsg(
liveMeeting.props.meetingProp.intId,
userId,
streamId
)
outGW.send(event)
}
}
}

View File

@ -1,20 +0,0 @@
package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.models.Webcams.{ removeViewer }
trait CamStreamUnsubscribedInSfuEvtMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleCamStreamUnsubscribedInSfuEvtMsg(msg: CamStreamUnsubscribedInSfuEvtMsg) {
// Subscriber's user ID
val userId = msg.header.userId
// Publisher's stream ID
val streamId = msg.body.streamId
removeViewer(liveMeeting.webcams, streamId, userId)
}
}

View File

@ -1,50 +0,0 @@
package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models.Webcams
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait EjectUserCamerasCmdMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleEjectUserCamerasCmdMsg(msg: EjectUserCamerasCmdMsg): Unit = {
val requesterUserId = msg.header.userId
val meetingId = liveMeeting.props.meetingProp.intId
val userToEject = msg.body.userId
val ejectionDisabled = !liveMeeting.props.usersProp.allowModsToEjectCameras
val isBreakout = liveMeeting.props.meetingProp.isBreakout
val badPermission = permissionFailed(
PermissionCheck.MOD_LEVEL,
PermissionCheck.VIEWER_LEVEL,
liveMeeting.users2x,
requesterUserId
)
if (ejectionDisabled || isBreakout || badPermission) {
val reason = "No permission to eject cameras from user."
PermissionCheck.ejectUserForFailedPermission(
meetingId,
requesterUserId,
reason,
outGW,
liveMeeting
)
} else {
log.info("Ejecting user cameras. meetingId=" + meetingId
+ " userId=" + userToEject
+ " requesterUserId=" + requesterUserId)
val broadcastedWebcams = Webcams.findWebcamsForUser(liveMeeting.webcams, userToEject)
broadcastedWebcams foreach { webcam =>
// Goes to SFU and comes back through CamBroadcastStoppedInSfuEvtMsg
val event = MsgBuilder.buildCamBroadcastStopSysMsg(
meetingId, userToEject, webcam.stream.id
)
outGW.send(event)
}
}
}
}

View File

@ -130,17 +130,6 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildGetWebcamStreamsMeetingRespMsg(meetingId: String, userId: String, streams: Vector[WebcamStreamVO]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetWebcamStreamsMeetingRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetWebcamStreamsMeetingRespMsg.NAME, meetingId, userId)
val body = GetWebcamStreamsMeetingRespMsgBody(streams)
val event = GetWebcamStreamsMeetingRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildGetVoiceUsersMeetingRespMsg(meetingId: String, userId: String, voiceUsers: Vector[VoiceConfUser]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetVoiceUsersMeetingRespMsg.NAME, routing)
@ -483,51 +472,6 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildGetCamSubscribePermissionRespMsg(
meetingId: String,
userId: String,
streamId: String,
sfuSessionId: String,
allowed: Boolean
): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetCamSubscribePermissionRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetCamSubscribePermissionRespMsg.NAME, meetingId, userId)
val body = GetCamSubscribePermissionRespMsgBody(
meetingId,
userId,
streamId,
sfuSessionId,
allowed
)
val event = GetCamSubscribePermissionRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildGetCamBroadcastPermissionRespMsg(
meetingId: String,
userId: String,
streamId: String,
sfuSessionId: String,
allowed: Boolean
): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetCamBroadcastPermissionRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetCamBroadcastPermissionRespMsg.NAME, meetingId, userId)
val body = GetCamBroadcastPermissionRespMsgBody(
meetingId,
userId,
streamId,
sfuSessionId,
allowed
)
val event = GetCamBroadcastPermissionRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildGetGlobalAudioPermissionRespMsg(
meetingId: String,
voiceConf: String,
@ -578,44 +522,6 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildCamBroadcastStopSysMsg(
meetingId: String,
userId: String,
streamId: String
): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(CamBroadcastStopSysMsg.NAME, routing)
val body = CamBroadcastStopSysMsgBody(meetingId, userId, streamId)
val header = BbbCoreBaseHeader(CamBroadcastStopSysMsg.NAME)
val event = CamBroadcastStopSysMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildCamStreamUnsubscribeSysMsg(
meetingId: String, userId: String, streamId: String
): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(CamStreamUnsubscribeSysMsg.NAME, routing)
val body = CamStreamUnsubscribeSysMsgBody(meetingId, userId, streamId)
val header = BbbCoreBaseHeader(CamStreamUnsubscribeSysMsg.NAME)
val event = CamStreamUnsubscribeSysMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildUserBroadcastCamStoppedEvtMsg(
meetingId: String, userId: String, streamId: String
): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(UserBroadcastCamStoppedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UserBroadcastCamStoppedEvtMsg.NAME, meetingId, userId)
val body = UserBroadcastCamStoppedEvtMsgBody(userId, streamId)
val event = UserBroadcastCamStoppedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildScreenBroadcastStopSysMsg(
meetingId: String,
voiceConf: String,

View File

@ -58,9 +58,9 @@ trait FakeTestData {
val rusers = Users2x.findAll(liveMeeting.users2x)
val others = rusers.filterNot(u => u.intId == ruser1.id)
val viewers = others.map { o => o.intId }
val wstream1 = FakeUserGenerator.createFakeWebcamStreamFor(ruser1.id, viewers.toSet)
Webcams.addWebcamBroadcastStream(liveMeeting.webcams, wstream1)
val subscribers = others.map { o => o.intId }
val wstream1 = FakeUserGenerator.createFakeWebcamStreamFor(ruser1.id, subscribers.toSet)
Webcams.addWebcamStream(liveMeeting.webcams, wstream1)
createFakeUser(liveMeeting, ruser1)
}

View File

@ -77,12 +77,9 @@ object FakeUserGenerator {
callerNum = name, muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime)
}
def createFakeWebcamStreamFor(userId: String, viewers: Set[String]): WebcamStream = {
def createFakeWebcamStreamFor(userId: String, subscribers: Set[String]): WebcamStream = {
val streamId = RandomStringGenerator.randomAlphanumericString(10)
val url = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" + streamId
val attributes = collection.immutable.HashMap("height" -> "600", "width" -> "800", "codec" -> "h264")
val media = MediaStream(streamId, url: String, userId: String, attributes, viewers)
WebcamStream(streamId, stream = media)
WebcamStream(streamId, userId, subscribers)
}
}

View File

@ -35,12 +35,9 @@ object TestDataGen {
callerNum = name, muted, talking, listenOnly)
}
def createFakeWebcamStreamFor(userId: String, viewers: Set[String]): WebcamStream = {
def createFakeWebcamStreamFor(userId: String, subscribers: Set[String]): WebcamStream = {
val streamId = RandomStringGenerator.randomAlphanumericString(10)
val url = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" + streamId
val attributes = collection.immutable.HashMap("height" -> "600", "width" -> "800", "codec" -> "h264")
val media = MediaStream(streamId, url: String, userId: String, attributes, viewers)
WebcamStream(streamId, stream = media)
WebcamStream(streamId, userId, subscribers)
}
def createUserFor(liveMeeting: LiveMeeting, regUser: RegisteredUser, presenter: Boolean): UserState = {

View File

@ -6,50 +6,34 @@ case class UserBroadcastCamStartedEvtMsg(
header: BbbClientMsgHeader,
body: UserBroadcastCamStartedEvtMsgBody
) extends BbbCoreMsg
case class UserBroadcastCamStartedEvtMsgBody(userId: String, stream: String)
case class UserBroadcastCamStartedEvtMsgBody(
userId: String,
stream: String
)
object UserBroadcastCamStartMsg { val NAME = "UserBroadcastCamStartMsg" }
case class UserBroadcastCamStartMsg(header: BbbClientMsgHeader, body: UserBroadcastCamStartMsgBody) extends StandardMsg
case class UserBroadcastCamStartMsg(
header: BbbClientMsgHeader,
body: UserBroadcastCamStartMsgBody
) extends StandardMsg
case class UserBroadcastCamStartMsgBody(stream: String)
object UserBroadcastCamStopMsg { val NAME = "UserBroadcastCamStopMsg" }
case class UserBroadcastCamStopMsg(header: BbbClientMsgHeader, body: UserBroadcastCamStopMsgBody) extends StandardMsg
case class UserBroadcastCamStopMsg(
header: BbbClientMsgHeader,
body: UserBroadcastCamStopMsgBody
) extends StandardMsg
case class UserBroadcastCamStopMsgBody(stream: String)
object UserBroadcastCamStoppedEvtMsg { val NAME = "UserBroadcastCamStoppedEvtMsg" }
case class UserBroadcastCamStoppedEvtMsg(header: BbbClientMsgHeader, body: UserBroadcastCamStoppedEvtMsgBody) extends BbbCoreMsg
case class UserBroadcastCamStoppedEvtMsgBody(userId: String, stream: String)
object GetWebcamStreamsMeetingRespMsg {
val NAME = "GetWebcamStreamsMeetingRespMsg"
def apply(meetingId: String, userId: String, streams: Vector[WebcamStreamVO]): GetWebcamStreamsMeetingRespMsg = {
val header = BbbClientMsgHeader(GetWebcamStreamsMeetingRespMsg.NAME, meetingId, userId)
val body = GetWebcamStreamsMeetingRespMsgBody(streams)
GetWebcamStreamsMeetingRespMsg(header, body)
}
}
case class GetWebcamStreamsMeetingRespMsg(header: BbbClientMsgHeader, body: GetWebcamStreamsMeetingRespMsgBody) extends BbbCoreMsg
case class GetWebcamStreamsMeetingRespMsgBody(streams: Vector[WebcamStreamVO])
case class WebcamStreamVO(streamId: String, stream: MediaStreamVO)
case class MediaStreamVO(id: String, url: String, userId: String, attributes: collection.immutable.Map[String, String], viewers: Set[String])
object GetWebcamStreamsRespMsg {
val NAME = "GetWebcamStreamsRespMsg"
def apply(meetingId: String, userId: String, webcamStreams: Vector[String]): GetWebcamStreamsRespMsg = {
val header = BbbClientMsgHeader(GetWebcamStreamsRespMsg.NAME, meetingId, userId)
val body = GetWebcamStreamsRespMsgBody(webcamStreams)
GetWebcamStreamsRespMsg(header, body)
}
}
case class GetWebcamStreamsRespMsg(header: BbbClientMsgHeader, body: GetWebcamStreamsRespMsgBody) extends BbbCoreMsg
case class GetWebcamStreamsRespMsgBody(webcamStreams: Vector[String])
case class UserBroadcastCamStoppedEvtMsg(
header: BbbClientMsgHeader,
body: UserBroadcastCamStoppedEvtMsgBody
) extends BbbCoreMsg
case class UserBroadcastCamStoppedEvtMsgBody(
userId: String,
stream: String
)
/* Sent by bbb-webrtc-sfu to ask permission for broadcasting a webcam
*/