feat: only record media while meeting is being actively recorded
Only record media (microphone, webcams and screens) while meeting is being actively recorded (ie an user has enabled recording in the conference). If the conference's recording is paused, media capture will stop as well (with appropriate recording events). A bigbluebutton.properties/API#create parameter called `recordFullDurationMedia` is added to control this behavior. The default is false (only capture while recording is active). Setting it to `true` enables the current (legacy) behavior: always capture media if the meeting's `recorded` prop is true.
This commit is contained in:
parent
c6f40a862e
commit
4d1aa87a88
@ -12,18 +12,30 @@ trait GetRecordingStatusReqMsgHdlr {
|
||||
|
||||
def handleGetRecordingStatusReqMsg(msg: GetRecordingStatusReqMsg) {
|
||||
|
||||
def buildGetRecordingStatusRespMsg(meetingId: String, userId: String, recorded: Boolean, recording: Boolean): BbbCommonEnvCoreMsg = {
|
||||
def buildGetRecordingStatusRespMsg(
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
recorded: Boolean,
|
||||
recording: Boolean,
|
||||
recordFullDurationMedia: Boolean
|
||||
): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(GetRecordingStatusRespMsg.NAME, routing)
|
||||
val body = GetRecordingStatusRespMsgBody(recorded, recording, userId)
|
||||
val body = GetRecordingStatusRespMsgBody(recorded, recording, recordFullDurationMedia, userId)
|
||||
val header = BbbClientMsgHeader(GetRecordingStatusRespMsg.NAME, meetingId, userId)
|
||||
val event = GetRecordingStatusRespMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
val event = buildGetRecordingStatusRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy,
|
||||
liveMeeting.props.recordProp.record, MeetingStatus2x.isRecording(liveMeeting.status))
|
||||
val event = buildGetRecordingStatusRespMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
msg.body.requestedBy,
|
||||
liveMeeting.props.recordProp.record,
|
||||
MeetingStatus2x.isRecording(liveMeeting.status),
|
||||
liveMeeting.props.recordProp.recordFullDurationMedia
|
||||
)
|
||||
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||
import org.bigbluebutton.core.apps.voice.VoiceApp
|
||||
|
||||
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
|
||||
this: UsersApp =>
|
||||
@ -49,6 +50,19 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
|
||||
outGW.send(notifyEvent)
|
||||
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
|
||||
// If meeting is not set to record full duration media, then we need to
|
||||
// start recording media here. Audio/FS recording is triggered here;
|
||||
// SFU intercepts this event and toggles rec for video and screen sharing.
|
||||
if (!liveMeeting.props.recordProp.recordFullDurationMedia) {
|
||||
log.info("Send START RECORDING voice conf. meetingId=" +
|
||||
liveMeeting.props.meetingProp.intId +
|
||||
" voice conf=" + liveMeeting.props.voiceProp.voiceConf)
|
||||
VoiceApp.startRecordingVoiceConference(
|
||||
liveMeeting,
|
||||
outGW
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
@ -61,6 +75,14 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
|
||||
outGW.send(notifyEvent)
|
||||
|
||||
MeetingStatus2x.recordingStopped(liveMeeting.status)
|
||||
|
||||
// If meeting is not set to record full duration media, then we need to stop recording
|
||||
if (!liveMeeting.props.recordProp.recordFullDurationMedia) {
|
||||
VoiceApp.stopRecordingVoiceConference(
|
||||
liveMeeting,
|
||||
outGW
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
|
||||
|
@ -11,9 +11,10 @@ import org.bigbluebutton.core.running.{LiveMeeting, MeetingActor, OutMsgRouter}
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core.apps.users.UsersApp
|
||||
import org.bigbluebutton.core.util.ColorPicker
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
||||
|
||||
object VoiceApp extends SystemConfiguration {
|
||||
|
||||
def genRecordPath(
|
||||
recordDir: String,
|
||||
meetingId: String,
|
||||
@ -35,18 +36,7 @@ object VoiceApp extends SystemConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingVoiceConference(liveMeeting: LiveMeeting, outGW: OutMsgRouter, stream: String): Unit = {
|
||||
MeetingStatus2x.voiceRecordingStart(liveMeeting.status, stream)
|
||||
val event = MsgBuilder.buildStartRecordingVoiceConfSysMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
liveMeeting.props.voiceProp.voiceConf,
|
||||
stream
|
||||
)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
def stopRecordingVoiceConference(liveMeeting: LiveMeeting, outGW: OutMsgRouter): Unit = {
|
||||
|
||||
val recStreams = MeetingStatus2x.getVoiceRecordingStreams(liveMeeting.status)
|
||||
|
||||
recStreams foreach { rs =>
|
||||
@ -58,6 +48,27 @@ object VoiceApp extends SystemConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingVoiceConference(
|
||||
liveMeeting: LiveMeeting,
|
||||
outGW: OutMsgRouter
|
||||
): Unit = {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val now = TimeUtil.timeNowInMs()
|
||||
val recordFile = genRecordPath(
|
||||
voiceConfRecordPath,
|
||||
meetingId,
|
||||
now,
|
||||
voiceConfRecordCodec
|
||||
)
|
||||
MeetingStatus2x.voiceRecordingStart(liveMeeting.status, recordFile)
|
||||
val event = MsgBuilder.buildStartRecordingVoiceConfSysMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
liveMeeting.props.voiceProp.voiceConf,
|
||||
recordFile
|
||||
)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
def broadcastUserMutedVoiceEvtMsg(
|
||||
meetingId: String,
|
||||
vu: VoiceUserState,
|
||||
|
@ -3,7 +3,8 @@ package org.bigbluebutton.core.apps.voice
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common2.msgs.VoiceConfRunningEvtMsg
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core.apps.voice.VoiceApp
|
||||
|
||||
trait VoiceConfRunningEvtMsgHdlr extends SystemConfiguration {
|
||||
this: BaseMeetingActor =>
|
||||
@ -15,17 +16,12 @@ trait VoiceConfRunningEvtMsgHdlr extends SystemConfiguration {
|
||||
log.info("Received VoiceConfRunningEvtMsg " + msg.body.running)
|
||||
|
||||
if (liveMeeting.props.recordProp.record) {
|
||||
if (msg.body.running) {
|
||||
if (msg.body.running &&
|
||||
(MeetingStatus2x.isRecording(liveMeeting.status) || liveMeeting.props.recordProp.recordFullDurationMedia)) {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val recordFile = VoiceApp.genRecordPath(
|
||||
voiceConfRecordPath,
|
||||
meetingId,
|
||||
TimeUtil.timeNowInMs(),
|
||||
voiceConfRecordCodec
|
||||
)
|
||||
log.info("Send START RECORDING voice conf. meetingId=" + meetingId + " voice conf=" + liveMeeting.props.voiceProp.voiceConf)
|
||||
|
||||
VoiceApp.startRecordingVoiceConference(liveMeeting, outGW, recordFile)
|
||||
VoiceApp.startRecordingVoiceConference(liveMeeting, outGW)
|
||||
} else {
|
||||
VoiceApp.stopRecordingVoiceConference(liveMeeting, outGW)
|
||||
}
|
||||
|
@ -779,6 +779,7 @@ class MeetingActor(
|
||||
val elapsedInMin = TimeUtil.millisToMinutes(elapsedInMs)
|
||||
|
||||
if (props.recordProp.record &&
|
||||
(MeetingStatus2x.isRecording(liveMeeting.status) || props.recordProp.recordFullDurationMedia) &&
|
||||
recordingChapterBreakLengthInMinutes > 0 &&
|
||||
elapsedInMin > recordingChapterBreakLengthInMinutes) {
|
||||
lastRecBreakSentOn = now
|
||||
@ -786,15 +787,7 @@ class MeetingActor(
|
||||
outGW.send(event)
|
||||
|
||||
VoiceApp.stopRecordingVoiceConference(liveMeeting, outGW)
|
||||
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val recordFile = VoiceApp.genRecordPath(
|
||||
voiceConfRecordPath,
|
||||
meetingId,
|
||||
now,
|
||||
voiceConfRecordCodec
|
||||
)
|
||||
VoiceApp.startRecordingVoiceConference(liveMeeting, outGW, recordFile)
|
||||
VoiceApp.startRecordingVoiceConference(liveMeeting, outGW)
|
||||
}
|
||||
}
|
||||
|
||||
@ -993,17 +986,15 @@ class MeetingActor(
|
||||
// Remove recording streams that have stopped so we should only have
|
||||
// one active recording stream.
|
||||
|
||||
// Let us start recording.
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val recordFile = VoiceApp.genRecordPath(
|
||||
voiceConfRecordPath,
|
||||
meetingId,
|
||||
TimeUtil.timeNowInMs(),
|
||||
voiceConfRecordCodec
|
||||
)
|
||||
log.info("Forcing START RECORDING voice conf. meetingId=" + meetingId + " voice conf=" + liveMeeting.props.voiceProp.voiceConf)
|
||||
// If the meeting is being actively recorded or recordFullDurationMedia is true
|
||||
// then we should start recording.
|
||||
if (MeetingStatus2x.isRecording(liveMeeting.status) ||
|
||||
liveMeeting.props.recordProp.recordFullDurationMedia) {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
log.info("Forcing START RECORDING voice conf. meetingId=" + meetingId + " voice conf=" + liveMeeting.props.voiceProp.voiceConf)
|
||||
|
||||
VoiceApp.startRecordingVoiceConference(liveMeeting, outGW, recordFile)
|
||||
VoiceApp.startRecordingVoiceConference(liveMeeting, outGW)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ case class BreakoutProps(
|
||||
|
||||
case class PasswordProp(moderatorPass: String, viewerPass: String, learningDashboardAccessToken: String)
|
||||
|
||||
case class RecordProp(record: Boolean, autoStartRecording: Boolean, allowStartStopRecording: Boolean, keepEvents: Boolean)
|
||||
case class RecordProp(record: Boolean, autoStartRecording: Boolean, allowStartStopRecording: Boolean, recordFullDurationMedia: Boolean, keepEvents: Boolean)
|
||||
|
||||
case class WelcomeProp(welcomeMsgTemplate: String, welcomeMsg: String, modOnlyMessage: String)
|
||||
|
||||
|
@ -124,7 +124,12 @@ case class GetRecordingStatusReqMsgBody(requestedBy: String)
|
||||
*/
|
||||
object GetRecordingStatusRespMsg { val NAME = "GetRecordingStatusRespMsg" }
|
||||
case class GetRecordingStatusRespMsg(header: BbbClientMsgHeader, body: GetRecordingStatusRespMsgBody) extends BbbCoreMsg
|
||||
case class GetRecordingStatusRespMsgBody(recorded: Boolean, recording: Boolean, requestedBy: String)
|
||||
case class GetRecordingStatusRespMsgBody(
|
||||
recorded: Boolean,
|
||||
recording: Boolean,
|
||||
recordFullDurationMedia: Boolean,
|
||||
requestedBy: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Sent by user to start recording mark.
|
||||
|
@ -22,6 +22,7 @@ trait TestFixtures {
|
||||
|
||||
val autoStartRecording = false
|
||||
val allowStartStopRecording = false
|
||||
val recordFullDurationMedia = false
|
||||
val webcamsOnlyForModerator = false
|
||||
val meetingCameraCap = 0
|
||||
val userCameraCap = 0
|
||||
@ -61,7 +62,10 @@ trait TestFixtures {
|
||||
userInactivityInspectTimerInMinutes = userInactivityInspectTimerInMinutes, userInactivityThresholdInMinutes = userInactivityInspectTimerInMinutes, userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes)
|
||||
val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword, learningDashboardAccessToken = learningDashboardAccessToken)
|
||||
val recordProp = RecordProp(record = record, autoStartRecording = autoStartRecording,
|
||||
allowStartStopRecording = allowStartStopRecording, keepEvents = keepEvents)
|
||||
allowStartStopRecording = allowStartStopRecording,
|
||||
recordFullDurationMedia = recordFullDurationMedia,
|
||||
keepEvents = keepEvents
|
||||
)
|
||||
val welcomeProp = WelcomeProp(welcomeMsgTemplate = welcomeMsgTemplate, welcomeMsg = welcomeMsg,
|
||||
modOnlyMessage = modOnlyMessage)
|
||||
val voiceProp = VoiceProp(telVoice = voiceConfId, voiceConf = voiceConfId, dialNumber = dialNumber, muteOnStart = muteOnStart)
|
||||
|
@ -107,6 +107,8 @@ public class ApiParams {
|
||||
public static final String END_WHEN_NO_MODERATOR = "endWhenNoModerator";
|
||||
public static final String END_WHEN_NO_MODERATOR_DELAY_IN_MINUTES = "endWhenNoModeratorDelayInMinutes";
|
||||
|
||||
public static final String RECORD_FULL_DURATION_MEDIA = "recordFullDurationMedia";
|
||||
|
||||
private ApiParams() {
|
||||
throw new IllegalStateException("ApiParams is a utility class. Instanciation is forbidden.");
|
||||
}
|
||||
|
@ -400,6 +400,7 @@ public class MeetingService implements MessageListener {
|
||||
|
||||
gw.createMeeting(m.getInternalId(), m.getExternalId(), m.getParentMeetingId(), m.getName(), m.isRecord(),
|
||||
m.getTelVoice(), m.getDuration(), m.getAutoStartRecording(), m.getAllowStartStopRecording(),
|
||||
m.getRecordFullDurationMedia(),
|
||||
m.getWebcamsOnlyForModerator(), m.getMeetingCameraCap(), m.getUserCameraCap(), m.getMaxPinnedCameras(), m.getModeratorPassword(), m.getViewerPassword(),
|
||||
m.getLearningDashboardAccessToken(), m.getCreateTime(),
|
||||
formatPrettyDate(m.getCreateTime()), m.isBreakout(), m.getSequence(), m.isFreeJoin(), m.getMetadata(),
|
||||
@ -1167,7 +1168,7 @@ public class MeetingService implements MessageListener {
|
||||
} else if (message instanceof GuestLobbyMessageChanged) {
|
||||
processGuestLobbyMessageChanged((GuestLobbyMessageChanged) message);
|
||||
} else if (message instanceof PrivateGuestLobbyMessageChanged) {
|
||||
processPrivateGuestLobbyMessageChanged((PrivateGuestLobbyMessageChanged) message);
|
||||
processPrivateGuestLobbyMessageChanged((PrivateGuestLobbyMessageChanged) message);
|
||||
} else if (message instanceof RecordChapterBreak) {
|
||||
processRecordingChapterBreak((RecordChapterBreak) message);
|
||||
} else if (message instanceof MakePresentationDownloadableMsg) {
|
||||
|
@ -91,6 +91,7 @@ public class ParamsProcessorUtil {
|
||||
private boolean disableRecordingDefault;
|
||||
private boolean autoStartRecording;
|
||||
private boolean allowStartStopRecording;
|
||||
private boolean recordFullDurationMedia;
|
||||
private boolean learningDashboardEnabled = true;
|
||||
private int learningDashboardCleanupDelayInMinutes;
|
||||
private boolean webcamsOnlyForModerator;
|
||||
@ -503,6 +504,18 @@ public class ParamsProcessorUtil {
|
||||
}
|
||||
}
|
||||
|
||||
boolean _recordFullDurationMedia = recordFullDurationMedia;
|
||||
if (!StringUtils.isEmpty(params.get(ApiParams.ALLOW_START_STOP_RECORDING))) {
|
||||
try {
|
||||
_recordFullDurationMedia = Boolean.parseBoolean(params
|
||||
.get(ApiParams.RECORD_FULL_DURATION_MEDIA));
|
||||
} catch (Exception ex) {
|
||||
log.warn(
|
||||
"Invalid param [recordFullDurationMedia] for meeting=[{}]",
|
||||
internalMeetingId);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Disabled Features
|
||||
ArrayList<String> listOfDisabledFeatures=new ArrayList(Arrays.asList(defaultDisabledFeatures.split(",")));
|
||||
if (!StringUtils.isEmpty(params.get(ApiParams.DISABLED_FEATURES))) {
|
||||
@ -555,7 +568,7 @@ public class ParamsProcessorUtil {
|
||||
int learningDashboardCleanupMins = 0;
|
||||
if(listOfDisabledFeatures.contains("learningDashboard") == false) {
|
||||
learningDashboardAccessToken = RandomStringUtils.randomAlphanumeric(12).toLowerCase();
|
||||
|
||||
|
||||
learningDashboardCleanupMins = learningDashboardCleanupDelayInMinutes;
|
||||
if (!StringUtils.isEmpty(params.get(ApiParams.LEARNING_DASHBOARD_CLEANUP_DELAY_IN_MINUTES))) {
|
||||
try {
|
||||
@ -735,6 +748,7 @@ public class ParamsProcessorUtil {
|
||||
.withDefaultAvatarURL(avatarURL)
|
||||
.withAutoStartRecording(autoStartRec)
|
||||
.withAllowStartStopRecording(allowStartStoptRec)
|
||||
.withRecordFullDurationMedia(_recordFullDurationMedia)
|
||||
.withWebcamsOnlyForModerator(webcamsOnlyForMod)
|
||||
.withMeetingCameraCap(meetingCameraCap)
|
||||
.withUserCameraCap(userCameraCap)
|
||||
@ -1226,6 +1240,10 @@ public class ParamsProcessorUtil {
|
||||
this.allowStartStopRecording = allowStartStopRecording;
|
||||
}
|
||||
|
||||
public void setRecordFullDurationMedia(boolean recordFullDurationMedia) {
|
||||
this.recordFullDurationMedia = recordFullDurationMedia;
|
||||
}
|
||||
|
||||
public void setLearningDashboardEnabled(boolean learningDashboardEnabled) {
|
||||
this.learningDashboardEnabled = learningDashboardEnabled;
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ public class Meeting {
|
||||
private boolean record;
|
||||
private boolean autoStartRecording = false;
|
||||
private boolean allowStartStopRecording = false;
|
||||
private boolean recordFullDurationMedia = false;
|
||||
private boolean haveRecordingMarks = false;
|
||||
private boolean webcamsOnlyForModerator = false;
|
||||
private Integer meetingCameraCap = 0;
|
||||
@ -148,6 +149,7 @@ public class Meeting {
|
||||
record = builder.record;
|
||||
autoStartRecording = builder.autoStartRecording;
|
||||
allowStartStopRecording = builder.allowStartStopRecording;
|
||||
recordFullDurationMedia = builder.recordFullDurationMedia;
|
||||
webcamsOnlyForModerator = builder.webcamsOnlyForModerator;
|
||||
meetingCameraCap = builder.meetingCameraCap;
|
||||
userCameraCap = builder.userCameraCap;
|
||||
@ -321,7 +323,7 @@ public class Meeting {
|
||||
public void setCaptureSlides(Boolean capture) {
|
||||
this.captureSlides = captureSlides;
|
||||
}
|
||||
|
||||
|
||||
public Boolean isCaptureNotes() {
|
||||
return captureNotes;
|
||||
}
|
||||
@ -582,6 +584,10 @@ public class Meeting {
|
||||
return allowStartStopRecording;
|
||||
}
|
||||
|
||||
public boolean getRecordFullDurationMedia() {
|
||||
return recordFullDurationMedia;
|
||||
}
|
||||
|
||||
public boolean getWebcamsOnlyForModerator() {
|
||||
return webcamsOnlyForModerator;
|
||||
}
|
||||
@ -862,6 +868,7 @@ public class Meeting {
|
||||
private int maxUsers;
|
||||
private boolean record;
|
||||
private boolean autoStartRecording;
|
||||
private boolean recordFullDurationMedia;
|
||||
private boolean allowStartStopRecording;
|
||||
private boolean webcamsOnlyForModerator;
|
||||
private Integer meetingCameraCap;
|
||||
@ -938,6 +945,11 @@ public class Meeting {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withRecordFullDurationMedia(boolean recordFullDurationMedia) {
|
||||
this.recordFullDurationMedia = recordFullDurationMedia;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withWebcamsOnlyForModerator(boolean only) {
|
||||
this.webcamsOnlyForModerator = only;
|
||||
return this;
|
||||
|
@ -7,7 +7,7 @@ import java.util.Map;
|
||||
public class CreateMeetingMessage {
|
||||
public static final String CREATE_MEETING_REQUEST_EVENT = "create_meeting_request";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
|
||||
public final String id;
|
||||
public final String externalId;
|
||||
public final String name;
|
||||
@ -16,6 +16,7 @@ public class CreateMeetingMessage {
|
||||
public final Long duration;
|
||||
public boolean autoStartRecording;
|
||||
public boolean allowStartStopRecording;
|
||||
public boolean recordFullDurationMedia;
|
||||
public boolean webcamsOnlyForModerator;
|
||||
public final Integer meetingCameraCap;
|
||||
public final Integer userCameraCap;
|
||||
@ -30,10 +31,11 @@ public class CreateMeetingMessage {
|
||||
public final Long createTime;
|
||||
public final String createDate;
|
||||
public final Map<String, String> metadata;
|
||||
|
||||
public CreateMeetingMessage(String id, String externalId, String name, Boolean record,
|
||||
String voiceBridge, Long duration,
|
||||
|
||||
public CreateMeetingMessage(String id, String externalId, String name, Boolean record,
|
||||
String voiceBridge, Long duration,
|
||||
Boolean autoStartRecording, Boolean allowStartStopRecording,
|
||||
Boolean recordFullDurationMedia,
|
||||
Boolean webcamsOnlyForModerator, Integer meetingCameraCap, Integer userCameraCap, Integer maxPinnedCameras, String moderatorPass,
|
||||
String viewerPass, String learningDashboardAccessToken,
|
||||
ArrayList<String> disabledFeatures,
|
||||
@ -49,6 +51,7 @@ public class CreateMeetingMessage {
|
||||
this.duration = duration;
|
||||
this.autoStartRecording = autoStartRecording;
|
||||
this.allowStartStopRecording = allowStartStopRecording;
|
||||
this.recordFullDurationMedia = recordFullDurationMedia;
|
||||
this.webcamsOnlyForModerator = webcamsOnlyForModerator;
|
||||
this.meetingCameraCap = meetingCameraCap;
|
||||
this.userCameraCap = userCameraCap;
|
||||
|
@ -11,11 +11,14 @@ public interface IPublisherService {
|
||||
void createMeeting(String meetingID, String externalMeetingID,
|
||||
String parentMeetingID, String meetingName, Boolean recorded,
|
||||
String voiceBridge, Integer duration, Boolean autoStartRecording,
|
||||
Boolean allowStartStopRecording, Boolean webcamsOnlyForModerator,
|
||||
Boolean allowStartStopRecording,
|
||||
Boolean recordFullDurationMedia,
|
||||
Boolean webcamsOnlyForModerator,
|
||||
Integer userCameraCap,
|
||||
String moderatorPass, String viewerPass, Long createTime,
|
||||
String createDate, Boolean isBreakout, Integer sequence,
|
||||
Boolean freeJoin, Map<String, String> metadata, String guestPolicy);
|
||||
Boolean freeJoin, Map<String, String> metadata, String guestPolicy
|
||||
);
|
||||
void endMeeting(String meetingId);
|
||||
void send(String channel, String message);
|
||||
void registerUser(String meetingID, String internalUserId, String fullname, String role, String externUserID,
|
||||
|
@ -18,7 +18,9 @@ public interface IBbbWebApiGWApp {
|
||||
void createMeeting(String meetingID, String externalMeetingID,
|
||||
String parentMeetingID, String meetingName, Boolean recorded,
|
||||
String voiceBridge, Integer duration, Boolean autoStartRecording,
|
||||
Boolean allowStartStopRecording, Boolean webcamsOnlyForModerator,
|
||||
Boolean allowStartStopRecording,
|
||||
Boolean recordFullDurationMedia,
|
||||
Boolean webcamsOnlyForModerator,
|
||||
Integer meetingCameraCap,
|
||||
Integer userCameraCap,
|
||||
Integer maxPinnedCameras,
|
||||
|
@ -5,12 +5,16 @@ public class RecordProp2 {
|
||||
public final boolean record;
|
||||
public final boolean autoStartRecording;
|
||||
public final boolean allowStartStopRecording;
|
||||
public final boolean recordFullDurationMedia;
|
||||
|
||||
public RecordProp2(boolean record,
|
||||
boolean autoStartRecording,
|
||||
boolean allowStartStopRecording) {
|
||||
boolean allowStartStopRecording,
|
||||
boolean recordFullDurationMedia
|
||||
) {
|
||||
this.record = record;
|
||||
this.autoStartRecording = autoStartRecording;
|
||||
this.allowStartStopRecording = allowStartStopRecording;
|
||||
this.recordFullDurationMedia = recordFullDurationMedia;
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,9 @@ class BbbWebApiGWApp(
|
||||
def createMeeting(meetingId: String, extMeetingId: String, parentMeetingId: String, meetingName: String,
|
||||
recorded: java.lang.Boolean, voiceBridge: String, duration: java.lang.Integer,
|
||||
autoStartRecording: java.lang.Boolean,
|
||||
allowStartStopRecording: java.lang.Boolean, webcamsOnlyForModerator: java.lang.Boolean,
|
||||
allowStartStopRecording: java.lang.Boolean,
|
||||
recordFullDurationMedia: java.lang.Boolean,
|
||||
webcamsOnlyForModerator: java.lang.Boolean,
|
||||
meetingCameraCap: java.lang.Integer,
|
||||
userCameraCap: java.lang.Integer,
|
||||
maxPinnedCameras: java.lang.Integer,
|
||||
@ -182,7 +184,9 @@ class BbbWebApiGWApp(
|
||||
|
||||
val password = PasswordProp(moderatorPass = moderatorPass, viewerPass = viewerPass, learningDashboardAccessToken = learningDashboardAccessToken)
|
||||
val recordProp = RecordProp(record = recorded.booleanValue(), autoStartRecording = autoStartRecording.booleanValue(),
|
||||
allowStartStopRecording = allowStartStopRecording.booleanValue(), keepEvents = keepEvents.booleanValue())
|
||||
allowStartStopRecording = allowStartStopRecording.booleanValue(),
|
||||
recordFullDurationMedia = recordFullDurationMedia.booleanValue(),
|
||||
keepEvents = keepEvents.booleanValue())
|
||||
|
||||
val breakoutProps = BreakoutProps(
|
||||
parentId = parentMeetingId,
|
||||
|
@ -1 +1 @@
|
||||
git clone --branch v2.9.12 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
||||
git clone --branch v2.9.13 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
||||
|
@ -231,6 +231,11 @@ autoStartRecording=false
|
||||
# Allow the user to start/stop recording.
|
||||
allowStartStopRecording=true
|
||||
|
||||
# Whether media (audio, cameras and screen sharing) should be captured on their
|
||||
# full duration if the meeting is recorded (recorded=true). Effectively ignores
|
||||
# the meeting's current recording state (paused/running).
|
||||
recordFullDurationMedia=false
|
||||
|
||||
# Number of minutes that Learning Dashboard will be available after the end of the meeting
|
||||
# if 0, the Learning Dashboard will keep available permanently
|
||||
# this is the default value, can be customized using the create API
|
||||
|
@ -158,6 +158,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<property name="disableRecordingDefault" value="${disableRecordingDefault}"/>
|
||||
<property name="autoStartRecording" value="${autoStartRecording}"/>
|
||||
<property name="allowStartStopRecording" value="${allowStartStopRecording}"/>
|
||||
<property name="recordFullDurationMedia" value="${recordFullDurationMedia}"/>
|
||||
<property name="learningDashboardEnabled" value="${learningDashboardEnabled}"/>
|
||||
<property name="learningDashboardCleanupDelayInMinutes" value="${learningDashboardCleanupDelayInMinutes}"/>
|
||||
<property name="webcamsOnlyForModerator" value="${webcamsOnlyForModerator}"/>
|
||||
|
@ -915,6 +915,7 @@ class ApiController {
|
||||
isBreakout meeting.isBreakout()
|
||||
logoutTimer meeting.getLogoutTimer()
|
||||
allowStartStopRecording meeting.getAllowStartStopRecording()
|
||||
recordFullDurationMedia meeting.getRecordFullDurationMedia()
|
||||
welcome us.welcome
|
||||
if (!StringUtils.isEmpty(meeting.moderatorOnlyMessage) && us.role.equals(ROLE_MODERATOR)) {
|
||||
modOnlyMessage meeting.moderatorOnlyMessage
|
||||
|
@ -1324,6 +1324,7 @@ These configs can be set in `/etc/bigbluebutton/bbb-web.properties`
|
||||
| `allowRequestsWithoutSession` | Allow requests without JSESSIONID to be handled | true/false | false |
|
||||
| `supportedChecksumAlgorithms` | List of supported hash algorithms for validating checksums | sha1, sha256, sha384, sha512 | sha1, sha256, sha384, sha512 |
|
||||
| `allowRevealOfBBBVersion` | Allow endpoint with current BigBlueButton version | true/false | false |
|
||||
| `recordFullDurationMedia` | Controls whether media should be captured on their full duration if the meeting's recorded property is true | true/false | false |
|
||||
|
||||
- _`overwritable`_: Config will be overwritten if the param is present in the API `/create` request
|
||||
|
||||
|
@ -377,6 +377,13 @@ const createEndpointTableData = [
|
||||
"required": false,
|
||||
"type": "String",
|
||||
"description": (<>Message to be displayed in presentation uploader modal describing how to use an external application to upload presentation files. Only works if <code className="language-plaintext highlighter-rouge">presentationUploadExternalUrl</code> is also set. (added 2.6)</>)
|
||||
},
|
||||
{
|
||||
"name": "recordFullDurationMedia",
|
||||
"required": false,
|
||||
"type": "Boolean",
|
||||
"default": "false",
|
||||
"description": (<>Controls whether media (audio, cameras and screen sharing) should be captured on their full duration if the meeting's recorded property is true (<code className="language-plaintext highlighter-rouge">recorded=true</code>). Default is false: only captures media while recording is running in the meeting. (added 2.6.9)</>)
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -96,7 +96,7 @@ Updated in 2.5:
|
||||
|
||||
Updated in 2.6:
|
||||
|
||||
- **create** - **Added:** `notifyRecordingIsOn`, `presentationUploadExternalUrl`, `presentationUploadExternalDescription`; Added `liveTranscription` and `presentation` as options for `disabledFeatures=`.
|
||||
- **create** - **Added:** `notifyRecordingIsOn`, `presentationUploadExternalUrl`, `presentationUploadExternalDescription`, `recordFullDurationMedia` (v2.6.9); Added `liveTranscription` and `presentation` as options for `disabledFeatures=`.
|
||||
|
||||
- **getRecordings** - **Added:** Added support for pagination using `offset`, `limit`
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user