Merge pull request #20703 from danielpetri1/webcam-background-url

feat: Accept custom webcamBackgroundURL
This commit is contained in:
Anton Georgiev 2024-07-23 15:55:56 -04:00 committed by GitHub
commit 5387fef101
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 199 additions and 109 deletions

View File

@ -57,7 +57,9 @@ trait RegisterUserReqMsgHdlr {
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId, val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
msg.body.name, msg.body.role, msg.body.authToken, msg.body.name, msg.body.role, msg.body.authToken,
msg.body.avatarURL, ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed, guestStatus, msg.body.excludeFromDashboard, false) msg.body.avatarURL,
msg.body.webcamBackgroundURL,
ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed, guestStatus, msg.body.excludeFromDashboard, false)
checkUserConcurrentAccesses(regUser) checkUserConcurrentAccesses(regUser)
@ -98,7 +100,7 @@ trait RegisterUserReqMsgHdlr {
val g = GuestApprovedVO(regUser.id, GuestStatus.ALLOW) val g = GuestApprovedVO(regUser.id, GuestStatus.ALLOW)
UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID) UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID)
case GuestStatus.WAIT => case GuestStatus.WAIT =>
val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.avatarURL, regUser.color, regUser.authed, regUser.registeredOn) val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.avatarURL, regUser.webcamBackgroundURL, regUser.color, regUser.authed, regUser.registeredOn)
addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting)
notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, liveMeeting.props.meetingProp.intId) notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, liveMeeting.props.meetingProp.intId)
val notifyEvent = MsgBuilder.buildNotifyRoleInMeetingEvtMsg( val notifyEvent = MsgBuilder.buildNotifyRoleInMeetingEvtMsg(

View File

@ -34,7 +34,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
def registerUserInRegisteredUsers() = { def registerUserInRegisteredUsers() = {
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId, val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
msg.body.callerIdName, Roles.VIEWER_ROLE, msg.body.intId, "", msg.body.callerIdName, Roles.VIEWER_ROLE, msg.body.intId, "", "",
userColor, true, true, GuestStatus.WAIT, true, false) userColor, true, true, GuestStatus.WAIT, true, false)
RegisteredUsers.add(liveMeeting.registeredUsers, regUser) RegisteredUsers.add(liveMeeting.registeredUsers, regUser)
} }
@ -57,6 +57,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
presenter = false, presenter = false,
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin, locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
avatar = "", avatar = "",
webcamBackground = "",
color = userColor, color = userColor,
clientType = "", clientType = "",
pickExempted = false, pickExempted = false,
@ -67,7 +68,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
def registerUserAsGuest() = { def registerUserAsGuest() = {
if (GuestsWaiting.findWithIntId(liveMeeting.guestsWaiting, msg.body.intId) == None) { if (GuestsWaiting.findWithIntId(liveMeeting.guestsWaiting, msg.body.intId) == None) {
val guest = GuestWaiting(msg.body.intId, msg.body.callerIdName, Roles.VIEWER_ROLE, true, "", userColor, true, System.currentTimeMillis()) val guest = GuestWaiting(msg.body.intId, msg.body.callerIdName, Roles.VIEWER_ROLE, true, "", "", userColor, true, System.currentTimeMillis())
GuestsWaiting.add(liveMeeting.guestsWaiting, guest) GuestsWaiting.add(liveMeeting.guestsWaiting, guest)
notifyModeratorsOfGuestWaiting(guest, liveMeeting.users2x, liveMeeting.props.meetingProp.intId) notifyModeratorsOfGuestWaiting(guest, liveMeeting.users2x, liveMeeting.props.meetingProp.intId)

View File

@ -6,47 +6,4 @@ trait UserJoinedVoiceConfMessageHdlr {
this: MeetingActor => this: MeetingActor =>
val outGW: OutMsgRouter val outGW: OutMsgRouter
/*
def startRecordingVoiceConference() {
if (Users.numUsersInVoiceConference(liveMeeting.users) == 1 &&
props.recordProp.record &&
!MeetingStatus2x.isVoiceRecording(liveMeeting.status)) {
MeetingStatus2x.startRecordingVoice(liveMeeting.status)
log.info("Send START RECORDING voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + props.voiceProp.voiceConf)
outGW.send(new StartRecordingVoiceConf(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf))
}
}
def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) = {
log.info("Received user joined voice. meetingId=" + props.meetingProp.intId + " callername=" + msg.callerIdName
+ " userId=" + msg.userId + " extUserId=" + msg.externUserId)
Users.findWithId(msg.userId, liveMeeting.users) match {
case Some(user) => {
// this is used to restore the mute state on reconnect
val previouslyMuted = user.voiceUser.muted
val nu = Users.restoreMuteState(user, liveMeeting.users, msg.voiceUserId, msg.userId, msg.callerIdName,
msg.callerIdNum, msg.muted, msg.talking, msg.avatarURL, msg.listenOnly)
log.info("User joined voice. meetingId=" + props.meetingProp.intId + " userId=" + user.id + " user=" + nu)
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status) || previouslyMuted) {
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
nu.id, nu.id, props.voiceProp.voiceConf,
nu.voiceUser.userId, true))
}
startRecordingVoiceConference()
}
case None => {
startRecordingVoiceConference()
}
}
}
*/
} }

View File

@ -68,7 +68,7 @@ class GuestsWaiting {
} }
} }
case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, color: String, authenticated: Boolean, registeredOn: Long) case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, webcamBackground: String, color: String, authenticated: Boolean, registeredOn: Long)
case class GuestPolicy(policy: String, setBy: String) case class GuestPolicy(policy: String, setBy: String)
object GuestPolicyType { object GuestPolicyType {

View File

@ -5,7 +5,7 @@ import org.bigbluebutton.core.domain.BreakoutRoom2x
object RegisteredUsers { object RegisteredUsers {
def create(userId: String, extId: String, name: String, roles: String, def create(userId: String, extId: String, name: String, roles: String,
token: String, avatar: String, color: String, guest: Boolean, authenticated: Boolean, token: String, avatar: String, webcamBackground: String, color: String, guest: Boolean, authenticated: Boolean,
guestStatus: String, excludeFromDashboard: Boolean, loggedOut: Boolean): RegisteredUser = { guestStatus: String, excludeFromDashboard: Boolean, loggedOut: Boolean): RegisteredUser = {
new RegisteredUser( new RegisteredUser(
userId, userId,
@ -14,6 +14,7 @@ object RegisteredUsers {
roles, roles,
token, token,
avatar, avatar,
webcamBackground,
color, color,
guest, guest,
authenticated, authenticated,
@ -198,6 +199,7 @@ case class RegisteredUser(
role: String, role: String,
authToken: String, authToken: String,
avatarURL: String, avatarURL: String,
webcamBackgroundURL: String,
color: String, color: String,
guest: Boolean, guest: Boolean,
authed: Boolean, authed: Boolean,

View File

@ -413,6 +413,7 @@ case class UserState(
locked: Boolean, locked: Boolean,
presenter: Boolean, presenter: Boolean,
avatar: String, avatar: String,
webcamBackground: String,
color: String, color: String,
roleChangedOn: Long = System.currentTimeMillis(), roleChangedOn: Long = System.currentTimeMillis(),
lastActivityTime: Long = System.currentTimeMillis(), lastActivityTime: Long = System.currentTimeMillis(),

View File

@ -70,6 +70,7 @@ trait HandlerHelpers extends SystemConfiguration {
presenter = false, presenter = false,
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin, locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
avatar = regUser.avatarURL, avatar = regUser.avatarURL,
webcamBackground = regUser.webcamBackgroundURL,
color = regUser.color, color = regUser.color,
clientType = clientType, clientType = clientType,
pickExempted = false, pickExempted = false,

View File

@ -16,7 +16,7 @@ object UserJoinedMeetingEvtMsgBuilder {
raiseHand = userState.raiseHand, raiseHand = userState.raiseHand,
away = userState.away, away = userState.away,
pin = userState.pin, pin = userState.pin,
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar, color = userState.color, presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar, webcamBackground = userState.webcamBackground, color = userState.color,
clientType = userState.clientType, userCustomData = userState.userCustomData) clientType = userState.clientType, userCustomData = userState.userCustomData)
val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body) val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body)

View File

@ -20,13 +20,13 @@ trait FakeTestData {
val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false, val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false,
talking = false, listenOnly = false) talking = false, listenOnly = false)
Users2x.add(liveMeeting.users2x, guest1) Users2x.add(liveMeeting.users2x, guest1)
val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, "", "#ff6242", guest1.authed, System.currentTimeMillis()) val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, "", "", "#ff6242", guest1.authed, System.currentTimeMillis())
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1) GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1)
val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false, val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false,
talking = false, listenOnly = false) talking = false, listenOnly = false)
Users2x.add(liveMeeting.users2x, guest2) Users2x.add(liveMeeting.users2x, guest2)
val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", "#ff6242", guest2.authed, System.currentTimeMillis()) val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", "", "#ff6242", guest2.authed, System.currentTimeMillis())
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2) GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2)
val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false) val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
@ -69,7 +69,8 @@ trait FakeTestData {
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false, UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false,
mobile = false, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus, mobile = false, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, locked = false, presenter = false, emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, locked = false, presenter = false,
avatar = regUser.avatarURL, color = "#ff6242", clientType = "unknown", avatar = regUser.avatarURL, webcamBackground = regUser.webcamBackgroundURL,
color = "#ff6242", clientType = "unknown",
pickExempted = false, userLeftFlag = UserLeftFlag(false, 0)) pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
} }

View File

@ -52,10 +52,12 @@ object FakeUserGenerator {
val authToken = RandomStringGenerator.randomAlphanumericString(16) val authToken = RandomStringGenerator.randomAlphanumericString(16)
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" + val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".png" RandomStringGenerator.randomAlphanumericString(10) + ".png"
val webcamBackgroundURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".jpg"
val color = "#ff6242" val color = "#ff6242"
val ru = RegisteredUsers.create(userId = id, extId, name, role, val ru = RegisteredUsers.create(userId = id, extId, name, role,
authToken, avatarURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, false) authToken, avatarURL, webcamBackgroundURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, false)
RegisteredUsers.add(users, ru) RegisteredUsers.add(users, ru)
ru ru
} }

View File

@ -12,9 +12,11 @@ object TestDataGen {
val authToken = RandomStringGenerator.randomAlphanumericString(16) val authToken = RandomStringGenerator.randomAlphanumericString(16)
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" + val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".png" RandomStringGenerator.randomAlphanumericString(10) + ".png"
val webcamBackgroundURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".jpg"
val ru = RegisteredUsers.create(userId = id, extId, name, role, val ru = RegisteredUsers.create(userId = id, extId, name, role,
authToken, avatarURL, guest, authed, GuestStatus.ALLOW, false) authToken, avatarURL, webcamBackgroundURL, guest, authed, GuestStatus.ALLOW, false)
RegisteredUsers.add(users, ru) RegisteredUsers.add(users, ru)
ru ru
@ -48,7 +50,7 @@ object TestDataGen {
val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, pin = false, mobile = false, emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, pin = false, mobile = false,
locked = false, presenter = false, avatar = regUser.avatarURL, color = "#ff6242", locked = false, presenter = false, avatar = regUser.avatarURL, regUser.webcamBackgroundURL, color = "#ff6242",
clientType = "unknown", pickExempted = false, userLeftFlag = UserLeftFlag(false, 0)) clientType = "unknown", pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
Users2x.add(liveMeeting.users2x, u) Users2x.add(liveMeeting.users2x, u)
u u

View File

@ -115,8 +115,8 @@ case class UserVO(id: String, externalId: String, name: String, role: String,
guest: Boolean, authed: Boolean, guestStatus: String, emojiStatus: String, guest: Boolean, authed: Boolean, guestStatus: String, emojiStatus: String,
presenter: Boolean, hasStream: Boolean, locked: Boolean, webcamStreams: Set[String], presenter: Boolean, hasStream: Boolean, locked: Boolean, webcamStreams: Set[String],
phoneUser: Boolean, voiceUser: VoiceUserVO, listenOnly: Boolean, avatarURL: String, phoneUser: Boolean, voiceUser: VoiceUserVO, listenOnly: Boolean, avatarURL: String,
joinedWeb: Boolean) webcamBackgroundURL: String, joinedWeb: Boolean)
case class VoiceUserVO(userId: String, webUserId: String, callerName: String, case class VoiceUserVO(userId: String, webUserId: String, callerName: String,
callerNum: String, joined: Boolean, locked: Boolean, muted: Boolean, callerNum: String, joined: Boolean, locked: Boolean, muted: Boolean,
talking: Boolean, avatarURL: String, listenOnly: Boolean) talking: Boolean, avatarURL: String, webcamBackgroundURL: String, listenOnly: Boolean)

View File

@ -6,7 +6,7 @@ case class RegisterUserReqMsg(
body: RegisterUserReqMsgBody body: RegisterUserReqMsgBody
) extends BbbCoreMsg ) extends BbbCoreMsg
case class RegisterUserReqMsgBody(meetingId: String, intUserId: String, name: String, role: String, case class RegisterUserReqMsgBody(meetingId: String, intUserId: String, name: String, role: String,
extUserId: String, authToken: String, avatarURL: String, extUserId: String, authToken: String, avatarURL: String, webcamBackgroundURL: String,
guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean, guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean,
userCustomData: Map[String, AnyRef]) userCustomData: Map[String, AnyRef])
@ -90,24 +90,25 @@ case class UserJoinedMeetingEvtMsg(
body: UserJoinedMeetingEvtMsgBody body: UserJoinedMeetingEvtMsgBody
) extends BbbCoreMsg ) extends BbbCoreMsg
case class UserJoinedMeetingEvtMsgBody( case class UserJoinedMeetingEvtMsgBody(
intId: String, intId: String,
extId: String, extId: String,
name: String, name: String,
role: String, role: String,
guest: Boolean, guest: Boolean,
authed: Boolean, authed: Boolean,
guestStatus: String, guestStatus: String,
emoji: String, emoji: String,
reactionEmoji: String, reactionEmoji: String,
raiseHand: Boolean, raiseHand: Boolean,
away: Boolean, away: Boolean,
pin: Boolean, pin: Boolean,
presenter: Boolean, presenter: Boolean,
locked: Boolean, locked: Boolean,
avatar: String, avatar: String,
color: String, webcamBackground: String,
clientType: String, color: String,
userCustomData: Map[String, String] clientType: String,
userCustomData: Map[String, String]
) )
/** /**

View File

@ -126,10 +126,10 @@ public class MeetingService implements MessageListener {
public void registerUser(String meetingID, String internalUserId, public void registerUser(String meetingID, String internalUserId,
String fullname, String role, String externUserID, String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest, String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest,
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby) { Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby) {
handle(new RegisterUser(meetingID, internalUserId, fullname, role, handle(new RegisterUser(meetingID, internalUserId, fullname, role,
externUserID, authToken, avatarURL, guest, authed, guestStatus, excludeFromDashboard, leftGuestLobby)); externUserID, authToken, avatarURL, webcamBackgroundURL, guest, authed, guestStatus, excludeFromDashboard, leftGuestLobby));
Meeting m = getMeeting(meetingID); Meeting m = getMeeting(meetingID);
if (m != null) { if (m != null) {
@ -437,7 +437,7 @@ public class MeetingService implements MessageListener {
gw.registerUser(message.meetingID, gw.registerUser(message.meetingID,
message.internalUserId, message.fullname, message.role, message.internalUserId, message.fullname, message.role,
message.externUserID, message.authToken, message.avatarURL, message.guest, message.externUserID, message.authToken, message.avatarURL, message.webcamBackgroundURL, message.guest,
message.authed, message.guestStatus, message.excludeFromDashboard, userCustomData); message.authed, message.guestStatus, message.excludeFromDashboard, userCustomData);
} }
@ -933,7 +933,7 @@ public class MeetingService implements MessageListener {
} }
User user = new User(message.userId, message.externalUserId, User user = new User(message.userId, message.externalUserId,
message.name, message.role, message.avatarURL, message.guest, message.guestStatus, message.name, message.role, message.avatarURL, message.webcamBackgroundURL, message.guest, message.guestStatus,
message.clientType); message.clientType);
if(m.getMaxUsers() > 0 && m.countUniqueExtIds() >= m.getMaxUsers()) { if(m.getMaxUsers() > 0 && m.countUniqueExtIds() >= m.getMaxUsers()) {
@ -1053,8 +1053,8 @@ public class MeetingService implements MessageListener {
} else { } else {
if (message.userId.startsWith("v_")) { if (message.userId.startsWith("v_")) {
// A dial-in user joined the meeting. Dial-in users by convention has userId that starts with "v_". // A dial-in user joined the meeting. Dial-in users by convention has userId that starts with "v_".
User vuser = new User(message.userId, message.userId, message.name, "DIAL-IN-USER", "", User vuser = new User(message.userId, message.userId, message.name, "DIAL-IN-USER", "", "",
true, GuestPolicy.ALLOW, "DIAL-IN"); true, GuestPolicy.ALLOW, "DIAL-IN");
vuser.setVoiceJoined(true); vuser.setVoiceJoined(true);
m.userJoined(vuser); m.userJoined(vuser);
} }

View File

@ -84,6 +84,8 @@ public class ParamsProcessorUtil {
private Integer defaultHttpSessionTimeout = 14400; private Integer defaultHttpSessionTimeout = 14400;
private Boolean useDefaultAvatar = false; private Boolean useDefaultAvatar = false;
private String defaultAvatarURL; private String defaultAvatarURL;
private Boolean useDefaultWebcamBackground = false;
private String defaultWebcamBackgroundURL;
private String defaultGuestPolicy; private String defaultGuestPolicy;
private Boolean authenticatedGuest; private Boolean authenticatedGuest;
private Boolean defaultAllowPromoteGuestToModerator; private Boolean defaultAllowPromoteGuestToModerator;
@ -741,6 +743,7 @@ public class ParamsProcessorUtil {
} }
String avatarURL = useDefaultAvatar ? defaultAvatarURL : ""; String avatarURL = useDefaultAvatar ? defaultAvatarURL : "";
String webcamBackgroundURL = useDefaultWebcamBackground ? defaultWebcamBackgroundURL : "";
int html5InstanceId = processHtml5InstanceId(params.get(ApiParams.HTML5_INSTANCE_ID)); int html5InstanceId = processHtml5InstanceId(params.get(ApiParams.HTML5_INSTANCE_ID));
@ -760,6 +763,7 @@ public class ParamsProcessorUtil {
.withTelVoice(telVoice).withWebVoice(webVoice) .withTelVoice(telVoice).withWebVoice(webVoice)
.withDialNumber(dialNumber) .withDialNumber(dialNumber)
.withDefaultAvatarURL(avatarURL) .withDefaultAvatarURL(avatarURL)
.withDefaultWebcamBackgroundURL(webcamBackgroundURL)
.withAutoStartRecording(autoStartRec) .withAutoStartRecording(autoStartRec)
.withAllowStartStopRecording(allowStartStoptRec) .withAllowStartStopRecording(allowStartStoptRec)
.withRecordFullDurationMedia(_recordFullDurationMedia) .withRecordFullDurationMedia(_recordFullDurationMedia)
@ -1296,6 +1300,14 @@ public class ParamsProcessorUtil {
this.defaultAvatarURL = url; this.defaultAvatarURL = url;
} }
public void setUseDefaultWebcamBackground(Boolean value) {
this.useDefaultWebcamBackground = value;
}
public void setDefaultWebcamBackgroundURL(String uri) {
this.defaultWebcamBackgroundURL = uri;
}
public void setDefaultGuestPolicy(String guestPolicy) { public void setDefaultGuestPolicy(String guestPolicy) {
this.defaultGuestPolicy = guestPolicy; this.defaultGuestPolicy = guestPolicy;
} }

View File

@ -79,6 +79,7 @@ public class Meeting {
private Integer maxPinnedCameras = 0; private Integer maxPinnedCameras = 0;
private String dialNumber; private String dialNumber;
private String defaultAvatarURL; private String defaultAvatarURL;
private String defaultWebcamBackgroundURL;
private String guestPolicy = GuestPolicy.ASK_MODERATOR; private String guestPolicy = GuestPolicy.ASK_MODERATOR;
private String guestLobbyMessage = ""; private String guestLobbyMessage = "";
private Map<String,String> usersWithGuestLobbyMessages; private Map<String,String> usersWithGuestLobbyMessages;
@ -148,6 +149,7 @@ public class Meeting {
logoutUrl = builder.logoutUrl; logoutUrl = builder.logoutUrl;
logoutTimer = builder.logoutTimer; logoutTimer = builder.logoutTimer;
defaultAvatarURL = builder.defaultAvatarURL; defaultAvatarURL = builder.defaultAvatarURL;
defaultWebcamBackgroundURL = builder.defaultWebcamBackgroundURL;
record = builder.record; record = builder.record;
autoStartRecording = builder.autoStartRecording; autoStartRecording = builder.autoStartRecording;
allowStartStopRecording = builder.allowStartStopRecording; allowStartStopRecording = builder.allowStartStopRecording;
@ -462,6 +464,10 @@ public class Meeting {
return defaultAvatarURL; return defaultAvatarURL;
} }
public String getDefaultWebcamBackgroundURL() {
return defaultWebcamBackgroundURL;
}
public void setWaitingPositionsInWaitingQueue(HashMap<String, String> guestUsersWithPositionInWaitingLine) { public void setWaitingPositionsInWaitingQueue(HashMap<String, String> guestUsersWithPositionInWaitingLine) {
this.guestUsersWithPositionInWaitingLine = guestUsersWithPositionInWaitingLine; this.guestUsersWithPositionInWaitingLine = guestUsersWithPositionInWaitingLine;
} }
@ -909,6 +915,7 @@ public class Meeting {
private Map<String, String> metadata; private Map<String, String> metadata;
private String dialNumber; private String dialNumber;
private String defaultAvatarURL; private String defaultAvatarURL;
private String defaultWebcamBackgroundURL;
private long createdTime; private long createdTime;
private boolean isBreakout; private boolean isBreakout;
private String guestPolicy; private String guestPolicy;
@ -1056,6 +1063,11 @@ public class Meeting {
return this; return this;
} }
public Builder withDefaultWebcamBackgroundURL(String w) {
defaultWebcamBackgroundURL = w;
return this;
}
public Builder isBreakout(Boolean b) { public Builder isBreakout(Boolean b) {
isBreakout = b; isBreakout = b;
return this; return this;

View File

@ -31,6 +31,7 @@ public class User {
private String fullname; private String fullname;
private String role; private String role;
private String avatarURL; private String avatarURL;
private String webcamBackgroundURL;
private Map<String,String> status; private Map<String,String> status;
private Boolean guest; private Boolean guest;
private String guestStatus; private String guestStatus;
@ -45,6 +46,7 @@ public class User {
String fullname, String fullname,
String role, String role,
String avatarURL, String avatarURL,
String webcamBackgroundURL,
Boolean guest, Boolean guest,
String guestStatus, String guestStatus,
String clientType) { String clientType) {
@ -53,6 +55,7 @@ public class User {
this.fullname = fullname; this.fullname = fullname;
this.role = role; this.role = role;
this.avatarURL = avatarURL; this.avatarURL = avatarURL;
this.webcamBackgroundURL = webcamBackgroundURL;
this.guest = guest; this.guest = guest;
this.guestStatus = guestStatus; this.guestStatus = guestStatus;
this.status = new ConcurrentHashMap<>(); this.status = new ConcurrentHashMap<>();
@ -128,6 +131,14 @@ public class User {
this.avatarURL = avatarURL; this.avatarURL = avatarURL;
} }
public String getWebcamBackgroundUrl() {
return webcamBackgroundURL;
}
public void setWebcamBackgroundUrl(String webcamBackgroundURL) {
this.webcamBackgroundURL = webcamBackgroundURL;
}
public boolean isModerator() { public boolean isModerator() {
return "MODERATOR".equalsIgnoreCase(this.role); return "MODERATOR".equalsIgnoreCase(this.role);
} }

View File

@ -42,6 +42,7 @@ public class UserSession {
public String logoutUrl = null; public String logoutUrl = null;
public String defaultLayout = "NOLAYOUT"; public String defaultLayout = "NOLAYOUT";
public String avatarURL; public String avatarURL;
public String webcamBackgroundURL;
public String guestStatus = GuestPolicy.ALLOW; public String guestStatus = GuestPolicy.ALLOW;
public String clientUrl = null; public String clientUrl = null;
public Boolean excludeFromDashboard = false; public Boolean excludeFromDashboard = false;
@ -134,6 +135,10 @@ public class UserSession {
return avatarURL; return avatarURL;
} }
public String getWebcamBackgroundURL() {
return webcamBackgroundURL;
}
public String getGuestStatus() { public String getGuestStatus() {
return guestStatus; return guestStatus;
} }

View File

@ -10,6 +10,7 @@ public class RegisterUser implements IMessage {
public final String externUserID; public final String externUserID;
public final String authToken; public final String authToken;
public final String avatarURL; public final String avatarURL;
public final String webcamBackgroundURL;
public final Boolean guest; public final Boolean guest;
public final Boolean authed; public final Boolean authed;
public final String guestStatus; public final String guestStatus;
@ -17,7 +18,7 @@ public class RegisterUser implements IMessage {
public final Boolean leftGuestLobby; public final Boolean leftGuestLobby;
public RegisterUser(String meetingID, String internalUserId, String fullname, String role, String externUserID, public RegisterUser(String meetingID, String internalUserId, String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest, String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest,
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby) { Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby) {
this.meetingID = meetingID; this.meetingID = meetingID;
this.internalUserId = internalUserId; this.internalUserId = internalUserId;
@ -25,7 +26,8 @@ public class RegisterUser implements IMessage {
this.role = role; this.role = role;
this.externUserID = externUserID; this.externUserID = externUserID;
this.authToken = authToken; this.authToken = authToken;
this.avatarURL = avatarURL; this.avatarURL = avatarURL;
this.webcamBackgroundURL = webcamBackgroundURL;
this.guest = guest; this.guest = guest;
this.authed = authed; this.authed = authed;
this.guestStatus = guestStatus; this.guestStatus = guestStatus;

View File

@ -7,6 +7,7 @@ public class UserJoined implements IMessage {
public final String name; public final String name;
public final String role; public final String role;
public final String avatarURL; public final String avatarURL;
public final String webcamBackgroundURL;
public final Boolean guest; public final Boolean guest;
public final String guestStatus; public final String guestStatus;
public final String clientType; public final String clientType;
@ -18,6 +19,7 @@ public class UserJoined implements IMessage {
String name, String name,
String role, String role,
String avatarURL, String avatarURL,
String webcamBackgroundURL,
Boolean guest, Boolean guest,
String guestStatus, String guestStatus,
String clientType) { String clientType) {
@ -27,6 +29,7 @@ public class UserJoined implements IMessage {
this.name = name; this.name = name;
this.role = role; this.role = role;
this.avatarURL = avatarURL; this.avatarURL = avatarURL;
this.webcamBackgroundURL = webcamBackgroundURL;
this.guest = guest; this.guest = guest;
this.guestStatus = guestStatus; this.guestStatus = guestStatus;
this.clientType = clientType; this.clientType = clientType;

View File

@ -22,7 +22,7 @@ public interface IPublisherService {
void endMeeting(String meetingId); void endMeeting(String meetingId);
void send(String channel, String message); void send(String channel, String message);
void registerUser(String meetingID, String internalUserId, String fullname, String role, String externUserID, void registerUser(String meetingID, String internalUserId, String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest, Boolean excludeFromDashboard, Boolean authed); String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest, Boolean excludeFromDashboard, Boolean authed);
void sendKeepAlive(String system, Long bbbWebTimestamp, Long akkaAppsTimestamp); void sendKeepAlive(String system, Long bbbWebTimestamp, Long akkaAppsTimestamp);
void sendStunTurnInfo(String meetingId, String internalUserId, Set<StunServer> stuns, Set<TurnEntry> turns); void sendStunTurnInfo(String meetingId, String internalUserId, Set<StunServer> stuns, Set<TurnEntry> turns);
} }

View File

@ -50,7 +50,7 @@ public interface IBbbWebApiGWApp {
String presentationUploadExternalUrl); String presentationUploadExternalUrl);
void registerUser(String meetingID, String internalUserId, String fullname, String role, void registerUser(String meetingID, String internalUserId, String fullname, String role,
String externUserID, String authToken, String avatarURL, String externUserID, String authToken, String avatarURL, String webcamBackgroundURL,
Boolean guest, Boolean authed, String guestStatus, Boolean excludeFromDashboard, Map<String, Object> userCustomData); Boolean guest, Boolean authed, String guestStatus, Boolean excludeFromDashboard, Map<String, Object> userCustomData);
void guestWaitingLeft(String meetingID, String internalUserId); void guestWaitingLeft(String meetingID, String internalUserId);

View File

@ -15,7 +15,7 @@ public interface IMeetingService {
void addUserSession(String token, UserSession user); void addUserSession(String token, UserSession user);
void registerUser(String meetingID, String internalUserId, void registerUser(String meetingID, String internalUserId,
String fullname, String role, String externUserID, String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest, Boolean authed); String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest, Boolean authed);
UserSession getUserSession(String token); UserSession getUserSession(String token);
UserSession removeUserSession(String token); UserSession removeUserSession(String token);
void purgeRegisteredUsers(); void purgeRegisteredUsers();

View File

@ -17,6 +17,7 @@ public class Meeting2 {
public final boolean forciblyEnded; public final boolean forciblyEnded;
public final String logoutUrl; public final String logoutUrl;
public final String defaultAvatarURL; public final String defaultAvatarURL;
public final String defaultWebcamBackgroundURL;
public final Map<String, String> metadata; public final Map<String, String> metadata;
public final List<String> breakoutRooms; public final List<String> breakoutRooms;
@ -30,6 +31,7 @@ public class Meeting2 {
boolean forciblyEnded, boolean forciblyEnded,
String logoutUrl, String logoutUrl,
String defaultAvatarURL, String defaultAvatarURL,
String defaultWebcamBackgroundURL,
Map<String, String> metadata, Map<String, String> metadata,
List<String> breakoutRooms) { List<String> breakoutRooms) {
this.props = props; this.props = props;
@ -42,6 +44,7 @@ public class Meeting2 {
this.forciblyEnded = forciblyEnded; this.forciblyEnded = forciblyEnded;
this.logoutUrl = logoutUrl; this.logoutUrl = logoutUrl;
this.defaultAvatarURL = defaultAvatarURL; this.defaultAvatarURL = defaultAvatarURL;
this.defaultWebcamBackgroundURL = defaultWebcamBackgroundURL;
this.metadata = metadata; this.metadata = metadata;
this.breakoutRooms = breakoutRooms; this.breakoutRooms = breakoutRooms;
} }

View File

@ -253,27 +253,23 @@ class BbbWebApiGWApp(
groupsAsVector groupsAsVector
) )
//meetingManagerActorRef ! new CreateMeetingMsg(defaultProps)
val event = MsgBuilder.buildCreateMeetingRequestToAkkaApps(defaultProps) val event = MsgBuilder.buildCreateMeetingRequestToAkkaApps(defaultProps)
msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, event)) msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, event))
} }
def registerUser(meetingId: String, intUserId: String, name: String, def registerUser(meetingId: String, intUserId: String, name: String,
role: String, extUserId: String, authToken: String, avatarURL: String, role: String, extUserId: String, authToken: String, avatarURL: String,
webcamBackgroundURL: String,
guest: java.lang.Boolean, authed: java.lang.Boolean, guest: java.lang.Boolean, authed: java.lang.Boolean,
guestStatus: String, excludeFromDashboard: java.lang.Boolean, userCustomData: java.util.Map[String, AnyRef]): Unit = { guestStatus: String, excludeFromDashboard: java.lang.Boolean, userCustomData: java.util.Map[String, AnyRef]): Unit = {
// meetingManagerActorRef ! new RegisterUser(meetingId = meetingId, intUserId = intUserId, name = name,
// role = role, extUserId = extUserId, authToken = authToken, avatarURL = avatarURL,
// guest = guest, authed = authed)
val ucd = userCustomData.asScala.toMap val ucd = userCustomData.asScala.toMap
val regUser = new RegisterUser(meetingId = meetingId, intUserId = intUserId, name = name, val regUser = new RegisterUser(meetingId = meetingId, intUserId = intUserId, name = name,
role = role, extUserId = extUserId, authToken = authToken, avatarURL = avatarURL, role = role, extUserId = extUserId, authToken = authToken, avatarURL = avatarURL,
guest = guest.booleanValue(), authed = authed.booleanValue(), guestStatus = guestStatus, webcamBackgroundURL = webcamBackgroundURL, guest = guest.booleanValue(),
authed = authed.booleanValue(), guestStatus = guestStatus,
excludeFromDashboard = excludeFromDashboard, ucd) excludeFromDashboard = excludeFromDashboard, ucd)
val event = MsgBuilder.buildRegisterUserRequestToAkkaApps(regUser) val event = MsgBuilder.buildRegisterUserRequestToAkkaApps(regUser)

View File

@ -40,7 +40,8 @@ object MsgBuilder {
val header = BbbCoreHeaderWithMeetingId(RegisterUserReqMsg.NAME, msg.meetingId) val header = BbbCoreHeaderWithMeetingId(RegisterUserReqMsg.NAME, msg.meetingId)
val body = RegisterUserReqMsgBody(meetingId = msg.meetingId, intUserId = msg.intUserId, val body = RegisterUserReqMsgBody(meetingId = msg.meetingId, intUserId = msg.intUserId,
name = msg.name, role = msg.role, extUserId = msg.extUserId, authToken = msg.authToken, name = msg.name, role = msg.role, extUserId = msg.extUserId, authToken = msg.authToken,
avatarURL = msg.avatarURL, guest = msg.guest, authed = msg.authed, guestStatus = msg.guestStatus, avatarURL = msg.avatarURL, webcamBackgroundURL = msg.webcamBackgroundURL,
guest = msg.guest, authed = msg.authed, guestStatus = msg.guestStatus,
excludeFromDashboard = msg.excludeFromDashboard, msg.userCustomData) excludeFromDashboard = msg.excludeFromDashboard, msg.userCustomData)
val req = RegisterUserReqMsg(header, body) val req = RegisterUserReqMsg(header, body)
BbbCommonEnvCoreMsg(envelope, req) BbbCommonEnvCoreMsg(envelope, req)

View File

@ -4,7 +4,8 @@ case class CallerId(name: String, number: String)
case class VoiceUser(id: String, callerId: CallerId, status: String, vid: String, wid: String, callingWith: String) case class VoiceUser(id: String, callerId: CallerId, status: String, vid: String, wid: String, callingWith: String)
case class User2(intId: String, extId: String, name: String, role: String, avatarURL: String, case class User2(intId: String, extId: String, name: String, role: String, avatarURL: String,
guest: Boolean, waitingForAcceptance: Boolean, status: Vector[String], webcamBackgroundURL: String,
guest: Boolean, waitingForAcceptance: Boolean, status: Vector[String],
streams: Set[String], customData: UserCustomData, voiceUser: VoiceUser, webcamStreams: Vector[String]) streams: Set[String], customData: UserCustomData, voiceUser: VoiceUser, webcamStreams: Vector[String])
object Users { object Users {
@ -40,7 +41,8 @@ class Users {
case class RegisteredUser2(meetingId: String, intId: String, name: String, role: String, case class RegisteredUser2(meetingId: String, intId: String, name: String, role: String,
extId: String, authToken: String, avatarURL: String, extId: String, authToken: String, avatarURL: String,
guest: Boolean, authed: Boolean) webcamBackgroundURL: String,
guest: Boolean, authed: Boolean)
object RegisteredUsers { object RegisteredUsers {
def findWithId(users: RegisteredUsers, id: String): Option[RegisteredUser2] = { def findWithId(users: RegisteredUsers, id: String): Option[RegisteredUser2] = {

View File

@ -16,7 +16,7 @@ case class CreateBreakoutRoomMsg(meetingId: String, parentMeetingId: String,
case class AddUserSession(token: String, session: UserSession) case class AddUserSession(token: String, session: UserSession)
case class RegisterUser(meetingId: String, intUserId: String, name: String, role: String, case class RegisterUser(meetingId: String, intUserId: String, name: String, role: String,
extUserId: String, authToken: String, avatarURL: String, extUserId: String, authToken: String, avatarURL: String, webcamBackgroundURL: String,
guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean, userCustomData: Map[String, AnyRef]) guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean, userCustomData: Map[String, AnyRef])
case class CreateMeetingMsg(defaultProps: DefaultProps) case class CreateMeetingMsg(defaultProps: DefaultProps)

View File

@ -122,10 +122,9 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
def handleUserJoinedMeetingEvtMsg(msg: UserJoinedMeetingEvtMsg): Unit = { def handleUserJoinedMeetingEvtMsg(msg: UserJoinedMeetingEvtMsg): Unit = {
olgMsgGW.handle(new UserJoined(msg.header.meetingId, msg.body.intId, olgMsgGW.handle(new UserJoined(msg.header.meetingId, msg.body.intId,
msg.body.extId, msg.body.name, msg.body.role, msg.body.avatar, msg.body.guest, msg.body.extId, msg.body.name, msg.body.role, msg.body.avatar, msg.body.webcamBackground, msg.body.guest,
msg.body.guestStatus, msg.body.guestStatus,
msg.body.clientType)) msg.body.clientType))
} }
def handlePresenterUnassignedEvtMsg(msg: PresenterUnassignedEvtMsg): Unit = { def handlePresenterUnassignedEvtMsg(msg: PresenterUnassignedEvtMsg): Unit = {

View File

@ -28,7 +28,8 @@ trait ToAkkaAppsSendersTrait extends SystemConfiguration {
val header = BbbCoreHeaderWithMeetingId(RegisterUserReqMsg.NAME, msg.meetingId) val header = BbbCoreHeaderWithMeetingId(RegisterUserReqMsg.NAME, msg.meetingId)
val body = RegisterUserReqMsgBody(meetingId = msg.meetingId, intUserId = msg.intUserId, val body = RegisterUserReqMsgBody(meetingId = msg.meetingId, intUserId = msg.intUserId,
name = msg.name, role = msg.role, extUserId = msg.extUserId, authToken = msg.authToken, name = msg.name, role = msg.role, extUserId = msg.extUserId, authToken = msg.authToken,
avatarURL = msg.avatarURL, guest = msg.guest, authed = msg.authed, guestStatus = msg.guestStatus, avatarURL = msg.avatarURL, webcamBackgroundURL = msg.webcamBackgroundURL, guest = msg.guest,
authed = msg.authed, guestStatus = msg.guestStatus,
excludeFromDashboard = msg.excludeFromDashboard, msg.userCustomData) excludeFromDashboard = msg.excludeFromDashboard, msg.userCustomData)
val req = RegisterUserReqMsg(header, body) val req = RegisterUserReqMsg(header, body)
val message = BbbCommonEnvCoreMsg(envelope, req) val message = BbbCommonEnvCoreMsg(envelope, req)

View File

@ -5,7 +5,7 @@ case class UserSession2(authToken: String, internalUserId: String, conferencenam
role: String, conference: String, room: String, guest: Boolean = false, role: String, conference: String, room: String, guest: Boolean = false,
authed: Boolean = false, voicebridge: String, webvoiceconf: String, authed: Boolean = false, voicebridge: String, webvoiceconf: String,
mode: String, record: String, welcome: String, logoutUrl: String, mode: String, record: String, welcome: String, logoutUrl: String,
defaultLayout: String = "NOLAYOUT", avatarURL: String) defaultLayout: String = "NOLAYOUT", avatarURL: String, webcamBackgroundURL: String)
class UserSessions { class UserSessions {

View File

@ -29,6 +29,7 @@ export default async function addUserPersistentData(user) {
presenter: Boolean, presenter: Boolean,
locked: Boolean, locked: Boolean,
avatar: String, avatar: String,
webcamBackground: String,
clientType: String, clientType: String,
left: Boolean, left: Boolean,
effectiveConnectionType: null, effectiveConnectionType: null,
@ -42,6 +43,7 @@ export default async function addUserPersistentData(user) {
role, role,
token, token,
avatar, avatar,
webcamBackground,
guest, guest,
color, color,
pin, pin,
@ -55,6 +57,7 @@ export default async function addUserPersistentData(user) {
role, role,
token, token,
avatar, avatar,
webcamBackground,
guest, guest,
color, color,
pin, pin,

View File

@ -30,6 +30,7 @@ export default async function addUser(meetingId, userData) {
presenter: Boolean, presenter: Boolean,
locked: Boolean, locked: Boolean,
avatar: String, avatar: String,
webcamBackground: String,
color: String, color: String,
pin: Boolean, pin: Boolean,
clientType: String, clientType: String,

View File

@ -1,3 +1,5 @@
import Auth from '/imports/ui/services/auth';
import Users from '/imports/api/users';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { import {
@ -325,6 +327,21 @@ class VideoPreview extends Component {
viewState: VIEW_STATES.found, viewState: VIEW_STATES.found,
}); });
this.displayPreview(); this.displayPreview();
// Set the custom or default virtual background
const webcamBackground = Users.findOne({
meetingId: Auth.meetingID,
userId: Auth.userID,
}, {
fields: {
webcamBackground: 1,
},
});
const webcamBackgroundURL = webcamBackground?.webcamBackground;
if (webcamBackgroundURL !== '') {
this.handleVirtualBgSelected(EFFECT_TYPES.IMAGE_TYPE, '', { url: webcamBackgroundURL });
}
}); });
} else { } else {
// There were no webcams coming from enumerateDevices. Throw an error. // There were no webcams coming from enumerateDevices. Throw an error.
@ -428,7 +445,6 @@ class VideoPreview extends Component {
// Resolves into true if the background switch is successful, false otherwise // Resolves into true if the background switch is successful, false otherwise
handleVirtualBgSelected(type, name, customParams) { handleVirtualBgSelected(type, name, customParams) {
const { sharedDevices } = this.props;
const { webcamDeviceId } = this.state; const { webcamDeviceId } = this.state;
const shared = this.isAlreadyShared(webcamDeviceId); const shared = this.isAlreadyShared(webcamDeviceId);

View File

@ -15,7 +15,6 @@ import {
getVirtualBgImagePath, getVirtualBgImagePath,
} from '/imports/ui/services/virtual-background/service' } from '/imports/ui/services/virtual-background/service'
import logger from '/imports/startup/client/logger'; import logger from '/imports/startup/client/logger';
import { simd } from 'wasm-feature-detect/dist/cjs/index'; import { simd } from 'wasm-feature-detect/dist/cjs/index';
const blurValue = '25px'; const blurValue = '25px';
@ -387,10 +386,44 @@ export async function createVirtualBackgroundService(parameters = null) {
parameters.backgroundType = 'blur'; parameters.backgroundType = 'blur';
parameters.isVirtualBackground = false; parameters.isVirtualBackground = false;
} else { } else {
parameters.virtualSource = virtualBackgroundImagePath + parameters.backgroundFilename;
if (parameters.customParams) { if (parameters.customParams) {
parameters.virtualSource = parameters.customParams.file; if (parameters.customParams.file) {
} else { parameters.virtualSource = parameters.customParams.file;
parameters.virtualSource = virtualBackgroundImagePath + parameters.backgroundFilename; } else {
const imageUrl = parameters.customParams.url;
// Function to convert image URL to a File object
async function getFileFromUrl(url) {
try {
const response = await fetch(url, {
credentials: 'omit',
mode: 'cors',
headers: {
'Accept': 'image/*',
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const blob = await response.blob();
const file = new File([blob], 'fetchedWebcamBackground', { type: blob.type });
return file;
} catch (error) {
logger.error('Fetch error:', error);
return null;
}
}
let fetchedWebcamBackground = await getFileFromUrl(imageUrl);
if (fetchedWebcamBackground) {
parameters.virtualSource = URL.createObjectURL(fetchedWebcamBackground);
} else {
logger.error('Failed to fetch custom webcam background image. Using fallback image.');
}
}
} }
} }

View File

@ -321,6 +321,10 @@ defaultGuestWaitURL=${bigbluebutton.web.serverURL}/html5client/guestWait
useDefaultAvatar=false useDefaultAvatar=false
defaultAvatarURL=${bigbluebutton.web.serverURL}/html5client/resources/images/avatar.png defaultAvatarURL=${bigbluebutton.web.serverURL}/html5client/resources/images/avatar.png
# The default webcam background image to display.
useDefaultWebcamBackground=false
defaultWebcamBackgroundURL=${bigbluebutton.web.serverURL}/html5client/resources/images/virtual-backgrounds/board.jpg
apiVersion=2.0 apiVersion=2.0
# Salt which is used by 3rd-party apps to authenticate api calls # Salt which is used by 3rd-party apps to authenticate api calls

View File

@ -163,6 +163,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="defaultMaxPinnedCameras" value="${maxPinnedCameras}"/> <property name="defaultMaxPinnedCameras" value="${maxPinnedCameras}"/>
<property name="useDefaultAvatar" value="${useDefaultAvatar}"/> <property name="useDefaultAvatar" value="${useDefaultAvatar}"/>
<property name="defaultAvatarURL" value="${defaultAvatarURL}"/> <property name="defaultAvatarURL" value="${defaultAvatarURL}"/>
<property name="useDefaultWebcamBackground" value="${useDefaultWebcamBackground}"/>
<property name="defaultWebcamBackgroundURL" value="${defaultWebcamBackgroundURL}"/>
<property name="defaultGuestPolicy" value="${defaultGuestPolicy}"/> <property name="defaultGuestPolicy" value="${defaultGuestPolicy}"/>
<property name="authenticatedGuest" value="${authenticatedGuest}"/> <property name="authenticatedGuest" value="${authenticatedGuest}"/>
<property name="defaultAllowPromoteGuestToModerator" value="${defaultAllowPromoteGuestToModerator}"/> <property name="defaultAllowPromoteGuestToModerator" value="${defaultAllowPromoteGuestToModerator}"/>

View File

@ -405,6 +405,12 @@ class ApiController {
us.avatarURL = meeting.defaultAvatarURL us.avatarURL = meeting.defaultAvatarURL
} }
if (!StringUtils.isEmpty(params.webcamBackgroundURL)) {
us.webcamBackgroundURL = params.webcamBackgroundURL;
} else {
us.webcamBackgroundURL = meeting.defaultWebcamBackgroundURL
}
if (!StringUtils.isEmpty(params.excludeFromDashboard)) { if (!StringUtils.isEmpty(params.excludeFromDashboard)) {
try { try {
us.excludeFromDashboard = Boolean.parseBoolean(params.excludeFromDashboard) us.excludeFromDashboard = Boolean.parseBoolean(params.excludeFromDashboard)
@ -435,6 +441,7 @@ class ApiController {
us.externUserID, us.externUserID,
us.authToken, us.authToken,
us.avatarURL, us.avatarURL,
us.webcamBackgroundURL,
us.guest, us.guest,
us.authed, us.authed,
guestStatusVal, guestStatusVal,
@ -941,6 +948,7 @@ class ApiController {
logoutUrl us.logoutUrl logoutUrl us.logoutUrl
defaultLayout us.defaultLayout defaultLayout us.defaultLayout
avatarURL us.avatarURL avatarURL us.avatarURL
webcamBackgroundURL us.webcamBackgroundURL
if (meeting.breakoutRoomsParams != null) { if (meeting.breakoutRoomsParams != null) {
breakoutRooms { breakoutRooms {
record meeting.breakoutRoomsParams.record record meeting.breakoutRoomsParams.record

View File

@ -55,6 +55,12 @@ const joinEndpointTableData = [
"type": "String", "type": "String",
"description": (<>The link for the users avatar to be displayed (default can be enabled/disabled and set with useDefaultAvatar and defaultAvatarURL in bbb-web.properties).</>) "description": (<>The link for the users avatar to be displayed (default can be enabled/disabled and set with useDefaultAvatar and defaultAvatarURL in bbb-web.properties).</>)
}, },
{
"name": "webcamBackgroundURL",
"required": false,
"type": "String",
"description": (<>The link for the user's webcam background to be displayed (default can be enabled/disabled and set with useDefaultWebcamBackground and defaultWebcamBackgroundURL in bigbluebutton.properties). Added in BigBlueButton 2.7.10.</>)
},
{ {
"name": "redirect", "name": "redirect",
"required": false, "required": false,