Merge pull request #14911 from GuiLeme/issue-9789
This commit is contained in:
commit
14815184e6
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.core.record.events
|
||||
|
||||
class MeetingConfigurationEvent extends StarterConfigurationEvent {
|
||||
import MeetingConfigurationEvent._
|
||||
|
||||
setEvent("MeetingConfigurationEvent")
|
||||
|
||||
def setWebcamsOnlyForModerator(webcamsOnlyForModerator: Boolean) {
|
||||
eventMap.put(WEBCAMS_ONLY_FOR_MODERATOR, webcamsOnlyForModerator.toString)
|
||||
}
|
||||
}
|
||||
|
||||
object MeetingConfigurationEvent {
|
||||
val WEBCAMS_ONLY_FOR_MODERATOR = "webcamsOnlyForModerator"
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.core.record.events
|
||||
|
||||
trait StarterConfigurationEvent extends RecordEvent {
|
||||
setModule("CONFIG")
|
||||
}
|
@ -116,6 +116,7 @@ class RedisRecorderActor(
|
||||
case m: RecordStatusResetSysMsg => handleRecordStatusResetSysMsg(m)
|
||||
case m: WebcamsOnlyForModeratorChangedEvtMsg => handleWebcamsOnlyForModeratorChangedEvtMsg(m)
|
||||
case m: MeetingEndingEvtMsg => handleEndAndKickAllSysMsg(m)
|
||||
case m: MeetingCreatedEvtMsg => handleStarterConfigurations(m)
|
||||
|
||||
// Recording
|
||||
case m: RecordingChapterBreakSysMsg => handleRecordingChapterBreakSysMsg(m)
|
||||
@ -622,4 +623,10 @@ class RedisRecorderActor(
|
||||
log.error("recording database is not available.")
|
||||
}
|
||||
|
||||
private def handleStarterConfigurations(msg: MeetingCreatedEvtMsg): Unit = {
|
||||
val ev = new MeetingConfigurationEvent()
|
||||
ev.setWebcamsOnlyForModerator(msg.body.props.usersProp.webcamsOnlyForModerator)
|
||||
record(msg.body.props.meetingProp.intId, ev.toMap().asJava)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import org.bigbluebutton.api.messaging.converters.messages.DeletedRecordingMessa
|
||||
import org.bigbluebutton.api.messaging.messages.*;
|
||||
import org.bigbluebutton.api2.IBbbWebApiGWApp;
|
||||
import org.bigbluebutton.api2.domain.UploadedTrack;
|
||||
import org.bigbluebutton.common2.msgs.MeetingCreatedEvtMsg;
|
||||
import org.bigbluebutton.common2.redis.RedisStorageService;
|
||||
import org.bigbluebutton.presentation.PresentationUrlDownloadService;
|
||||
import org.bigbluebutton.presentation.imp.SwfSlidesGenerationProgressNotifier;
|
||||
|
@ -112,8 +112,60 @@ module BigBlueButton
|
||||
start_events
|
||||
end
|
||||
|
||||
def self.to_boolean(obj)
|
||||
return obj.to_s.downcase == "true"
|
||||
end
|
||||
|
||||
def self.is_user_moderator(user_id, list_user_info)
|
||||
user_role = list_user_info[user_id]
|
||||
if !user_role.nil?
|
||||
if user_role == "MODERATOR"
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def self.get_id_from_filename(filename)
|
||||
return filename.split("/")[-1].split("-")[1]
|
||||
end
|
||||
|
||||
def self.extract_filename_from_userId(userId, filenames_list)
|
||||
filename_return = ""
|
||||
filenames_list.each do |filename|
|
||||
if !filename.match(userId).nil?
|
||||
filename_return = filename
|
||||
end
|
||||
end
|
||||
return filename_return
|
||||
end
|
||||
|
||||
def self.process_webcamsOnlyForModerator(list_user_info, active_videos, inactive_videos, webcamsOnlyForModerator)
|
||||
|
||||
if webcamsOnlyForModerator
|
||||
list_user_info.each do |user_id, user_role|
|
||||
# If the user is a viewer:
|
||||
if !BigBlueButton::Events.is_user_moderator(user_id, list_user_info)
|
||||
filename = BigBlueButton::Events.extract_filename_from_userId(user_id, active_videos)
|
||||
if filename != ""
|
||||
active_videos.delete(filename)
|
||||
inactive_videos << filename
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
# If the WebcamsOnlyForModerator is false, all previously inactive videos will become active
|
||||
inactive_videos.each do |filename|
|
||||
active_videos << filename
|
||||
end
|
||||
inactive_videos.clear
|
||||
end
|
||||
end
|
||||
|
||||
# Build a webcam EDL
|
||||
def self.create_webcam_edl(events, archive_dir)
|
||||
def self.create_webcam_edl(events, archive_dir, show_moderator_viewpoint)
|
||||
recording = events.at_xpath('/recording')
|
||||
meeting_id = recording['meeting_id']
|
||||
event = events.at_xpath('/recording/event[position()=1]')
|
||||
@ -125,57 +177,203 @@ module BigBlueButton
|
||||
|
||||
videos = {}
|
||||
active_videos = []
|
||||
inactive_videos = []
|
||||
video_edl = []
|
||||
|
||||
video_edl << {
|
||||
:timestamp => 0,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
list_user_info = {}
|
||||
webcamsOnlyForModerator = false
|
||||
if show_moderator_viewpoint
|
||||
events.xpath('/recording/event[@module="WEBCAM" or (@module="bbb-webrtc-sfu" and (@eventname="StartWebRTCShareEvent" or @eventname="StopWebRTCShareEvent"))]').each do |event|
|
||||
timestamp = event['timestamp'].to_i - initial_timestamp
|
||||
# Determine the video filename
|
||||
case event['eventname']
|
||||
when 'StartWebcamShareEvent', 'StopWebcamShareEvent'
|
||||
stream = event.at_xpath('stream').text
|
||||
filename = "#{video_dir}/#{stream}.flv"
|
||||
when 'StartWebRTCShareEvent', 'StopWebRTCShareEvent'
|
||||
uri = event.at_xpath('filename').text
|
||||
filename = "#{video_dir}/#{File.basename(uri)}"
|
||||
end
|
||||
raise "Couldn't determine webcam filename" if filename.nil?
|
||||
|
||||
events.xpath('/recording/event[@module="WEBCAM" or (@module="bbb-webrtc-sfu" and (@eventname="StartWebRTCShareEvent" or @eventname="StopWebRTCShareEvent"))]').each do |event|
|
||||
timestamp = event['timestamp'].to_i - initial_timestamp
|
||||
# Determine the video filename
|
||||
case event['eventname']
|
||||
when 'StartWebcamShareEvent', 'StopWebcamShareEvent'
|
||||
stream = event.at_xpath('stream').text
|
||||
filename = "#{video_dir}/#{stream}.flv"
|
||||
when 'StartWebRTCShareEvent', 'StopWebRTCShareEvent'
|
||||
uri = event.at_xpath('filename').text
|
||||
filename = "#{video_dir}/#{File.basename(uri)}"
|
||||
# Add the video to the EDL
|
||||
case event['eventname']
|
||||
when 'StartWebcamShareEvent', 'StartWebRTCShareEvent'
|
||||
videos[filename] = { :timestamp => timestamp }
|
||||
active_videos << filename
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp]
|
||||
}
|
||||
end
|
||||
video_edl << edl_entry
|
||||
when 'StopWebcamShareEvent', 'StopWebRTCShareEvent'
|
||||
active_videos.delete(filename)
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp]
|
||||
}
|
||||
end
|
||||
video_edl << edl_entry
|
||||
end
|
||||
end
|
||||
raise "Couldn't determine webcam filename" if filename.nil?
|
||||
else
|
||||
events.xpath('/recording/event[@module="WEBCAM" or (@module="bbb-webrtc-sfu" and (@eventname="StartWebRTCShareEvent" or @eventname="StopWebRTCShareEvent")) or (@module="PARTICIPANT" and (@eventname="ParticipantStatusChangeEvent" or @eventname="ParticipantJoinEvent")) or @eventname="WebcamsOnlyForModeratorEvent" or @eventname="MeetingConfigurationEvent"]').each do |event|
|
||||
timestamp = event['timestamp'].to_i - initial_timestamp
|
||||
|
||||
# Add the video to the EDL
|
||||
case event['eventname']
|
||||
when 'StartWebcamShareEvent', 'StartWebRTCShareEvent'
|
||||
videos[filename] = { :timestamp => timestamp }
|
||||
active_videos << filename
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp]
|
||||
}
|
||||
# Determine the video filename if event is as the following
|
||||
case event['eventname']
|
||||
when 'StartWebcamShareEvent', 'StopWebcamShareEvent'
|
||||
stream = event.at_xpath('stream').text
|
||||
filename = "#{video_dir}/#{stream}.flv"
|
||||
when 'StartWebRTCShareEvent', 'StopWebRTCShareEvent'
|
||||
uri = event.at_xpath('filename').text
|
||||
filename = "#{video_dir}/#{File.basename(uri)}"
|
||||
end
|
||||
video_edl << edl_entry
|
||||
when 'StopWebcamShareEvent', 'StopWebRTCShareEvent'
|
||||
active_videos.delete(filename)
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp]
|
||||
# Add the video to the EDL
|
||||
case event['eventname']
|
||||
when 'StartWebcamShareEvent', 'StartWebRTCShareEvent'
|
||||
userId = BigBlueButton::Events.get_id_from_filename(filename)
|
||||
is_in_forbidden_period = webcamsOnlyForModerator
|
||||
|
||||
if (!is_in_forbidden_period) || (is_in_forbidden_period && BigBlueButton::Events.is_user_moderator(userId, list_user_info))
|
||||
|
||||
videos[filename] = { :timestamp => timestamp }
|
||||
active_videos << filename
|
||||
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp],
|
||||
:user_id => BigBlueButton::Events.get_id_from_filename(filename)
|
||||
}
|
||||
end
|
||||
video_edl << edl_entry
|
||||
elsif is_in_forbidden_period && !BigBlueButton::Events.is_user_moderator(userId, list_user_info)
|
||||
inactive_videos << filename
|
||||
videos[filename] = { :timestamp => timestamp }
|
||||
end
|
||||
when 'StopWebcamShareEvent', 'StopWebRTCShareEvent'
|
||||
userId = BigBlueButton::Events.get_id_from_filename(filename)
|
||||
is_in_forbidden_period = webcamsOnlyForModerator
|
||||
|
||||
if (!is_in_forbidden_period) || (is_in_forbidden_period && BigBlueButton::Events.is_user_moderator(userId, list_user_info))
|
||||
active_videos.delete(filename)
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp],
|
||||
:user_id => BigBlueButton::Events.get_id_from_filename(filename)
|
||||
}
|
||||
end
|
||||
video_edl << edl_entry
|
||||
elsif is_in_forbidden_period && !BigBlueButton::Events.is_user_moderator(userId, list_user_info)
|
||||
inactive_videos.delete(filename)
|
||||
end
|
||||
when "ParticipantJoinEvent"
|
||||
user_id = event.at_xpath('userId').text
|
||||
list_user_info[user_id] = event.at_xpath('role').text
|
||||
|
||||
when "ParticipantStatusChangeEvent"
|
||||
is_in_forbidden_period = webcamsOnlyForModerator
|
||||
userId = ""
|
||||
filename_to_add = ""
|
||||
|
||||
if event.at_xpath('status').text == "role"
|
||||
userId = event.at_xpath('userId').text
|
||||
|
||||
if is_in_forbidden_period && event.at_xpath('value').text == "MODERATOR"
|
||||
filename_to_add = BigBlueButton::Events.extract_filename_from_userId(userId, inactive_videos)
|
||||
if filename_to_add != ""
|
||||
inactive_videos.delete(filename_to_add)
|
||||
active_videos << filename_to_add
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp],
|
||||
:user_id => userId
|
||||
}
|
||||
end
|
||||
video_edl << edl_entry
|
||||
end
|
||||
elsif is_in_forbidden_period && event.at_xpath('value').text == "VIEWER"
|
||||
filename_to_add = BigBlueButton::Events.extract_filename_from_userId(userId, active_videos)
|
||||
if filename != ""
|
||||
active_videos.delete(filename_to_add)
|
||||
inactive_videos << filename_to_add
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp],
|
||||
:user_id => userId
|
||||
}
|
||||
end
|
||||
video_edl << edl_entry
|
||||
end
|
||||
end
|
||||
user_id = event.at_xpath('userId').text
|
||||
list_user_info[user_id] = event.at_xpath('value').text
|
||||
end
|
||||
|
||||
when "MeetingConfigurationEvent"
|
||||
webcamsOnlyForModerator = BigBlueButton::Events.to_boolean(event.at_xpath('webcamsOnlyForModerator').text)
|
||||
|
||||
when "WebcamsOnlyForModeratorEvent"
|
||||
# Change active and inactive videos.
|
||||
BigBlueButton::Events.process_webcamsOnlyForModerator(list_user_info, active_videos, inactive_videos, BigBlueButton::Events.to_boolean(event.at_xpath("webcamsOnlyForModerator").text))
|
||||
|
||||
edl_entry = {
|
||||
:timestamp => timestamp,
|
||||
:areas => { :webcam => [] }
|
||||
}
|
||||
active_videos.each do |filename|
|
||||
edl_entry[:areas][:webcam] << {
|
||||
:filename => filename,
|
||||
:timestamp => timestamp - videos[filename][:timestamp],
|
||||
:user_id => userId
|
||||
}
|
||||
end
|
||||
video_edl << edl_entry
|
||||
|
||||
webcamsOnlyForModerator = BigBlueButton::Events.to_boolean(event.at_xpath('webcamsOnlyForModerator').text)
|
||||
end
|
||||
video_edl << edl_entry
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,7 +28,7 @@ require File.expand_path('../../edl', __FILE__)
|
||||
module BigBlueButton
|
||||
|
||||
|
||||
def BigBlueButton.process_webcam_videos(target_dir, raw_archive_dir, output_width, output_height, output_framerate, audio_offset, processed_audio_file, video_formats=['webm'])
|
||||
def BigBlueButton.process_webcam_videos(target_dir, raw_archive_dir, output_width, output_height, output_framerate, audio_offset, processed_audio_file, video_formats=['webm'], show_moderator_viewpoint=false)
|
||||
BigBlueButton.logger.info("Processing webcam videos")
|
||||
|
||||
# raw_archive_dir already contains meeting_id
|
||||
@ -38,7 +38,7 @@ module BigBlueButton
|
||||
start_time = BigBlueButton::Events.first_event_timestamp(events)
|
||||
end_time = BigBlueButton::Events.last_event_timestamp(events)
|
||||
webcam_edl = BigBlueButton::Events.create_webcam_edl(
|
||||
events, raw_archive_dir)
|
||||
events, raw_archive_dir, show_moderator_viewpoint)
|
||||
user_video_edl = BigBlueButton::Events.edl_match_recording_marks_video(
|
||||
webcam_edl, events, start_time, end_time)
|
||||
BigBlueButton::EDL::Video.dump(user_video_edl)
|
||||
|
@ -40,6 +40,11 @@ anonymize_chat: false
|
||||
# meta param: meta_bbb-anonymize-chat-moderators (true/false)
|
||||
anonymize_chat_moderators: false
|
||||
|
||||
# By default, recordings assume the Viewer viewpoint (with all locks)
|
||||
# so when webcamsOnlyForModerator=true, only moderators webcams are included in recordings.
|
||||
# Use this option to ignore locks and assume Moderator viewpoint instead.
|
||||
show_moderator_viewpoint: false
|
||||
|
||||
# Sequence of recording steps. Keys are the current step, values
|
||||
# are the next step(s). Examples:
|
||||
# current_step: next_step
|
||||
|
@ -225,7 +225,7 @@ unless FileTest.directory?(target_dir)
|
||||
|
||||
webcam_framerate = 15 if webcam_framerate.nil?
|
||||
processed_audio_file = BigBlueButton::AudioProcessor.get_processed_audio_file(raw_archive_dir, "#{target_dir}/audio")
|
||||
BigBlueButton.process_webcam_videos(target_dir, raw_archive_dir, webcam_width, webcam_height, webcam_framerate, presentation_props['audio_offset'], processed_audio_file, presentation_props['video_formats'])
|
||||
BigBlueButton.process_webcam_videos(target_dir, raw_archive_dir, webcam_width, webcam_height, webcam_framerate, presentation_props['audio_offset'], processed_audio_file, presentation_props['video_formats'], props['show_moderator_viewpoint'])
|
||||
end
|
||||
|
||||
if !Dir["#{raw_archive_dir}/deskshare/*"].empty? && presentation_props['include_deskshare']
|
||||
|
@ -84,7 +84,7 @@ begin
|
||||
logger.info "Generating video events list"
|
||||
|
||||
# Webcams
|
||||
webcam_edl = BigBlueButton::Events.create_webcam_edl(events, raw_archive_dir)
|
||||
webcam_edl = BigBlueButton::Events.create_webcam_edl(events, raw_archive_dir, props['show_moderator_viewpoint'])
|
||||
logger.debug "Webcam EDL:"
|
||||
BigBlueButton::EDL::Video.dump(webcam_edl)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user