Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,18 @@ def self.get_external_video_events(events_xml)
s = { :timestamp => event['timestamp'].to_i }
external_videos_events << s
end
# See: https://github.com/bigbluebutton/bbb-playback/pull/127
# You need to directly modify the script /usr/local/bigbluebutton/core/lib/recordandplayback/generators/events.rb
events_xml.xpath("recording/event[@eventname='UpdateExternalVideoRecordEvent']").each do |event|
s = {
:timestamp => event['timestamp'].to_i,
:rate => event.at_xpath("rate").text.to_f,
:state => event.at_xpath("state").text.to_i,
:status => event.at_xpath("status").text,
:time => event.at_xpath("time").text.to_f,
}
external_videos_events << s
end
external_videos_events.sort_by {|a| a[:timestamp]}
end

Expand All @@ -913,10 +925,15 @@ def self.get_start_and_stop_rec_events(events_xml, allow_empty_events=false)
def self.get_start_and_stop_external_video_events(events_xml)
BigBlueButton.logger.info "Getting start and stop externalvideo events"
external_video_events = BigBlueButton::Events.get_external_video_events(events_xml)
if external_video_events.size.odd?
n_start_and_stop_events = 0
external_video_events.each do |e|
n_start_and_stop_events += 1 unless e[:status]
end
if n_start_and_stop_events.odd?
# user did not click to stop external video before ending meeting
external_video_events << { :timestamp => BigBlueButton::Events.last_event_timestamp(events_xml) }
end
#BigBlueButton.logger.info "get_start_and_stop_external_video_events: #{external_video_events.sort_by {|a| a[:timestamp]}}"
external_video_events.sort_by {|a| a[:timestamp]}
end

Expand All @@ -935,19 +952,31 @@ def self.match_start_and_stop_rec_events(rec_events)
matched_rec_events
end

# Match external video start and stop events
def self.match_start_and_stop_external_video_events(external_video_events)
# Match external video start, update, and stop events
def self.match_all_external_video_events(external_video_events)
BigBlueButton.logger.info ("Matching external video events")
matched_external_video_events = []
external_video_events.each_with_index do |evt,i|
if i.even?
external_video_events.each do |evt|
if evt[:status] # Update
matched_external_video_events[-1][:updates] << {
:timestamp => evt[:timestamp],
:rate => evt[:rate],
:state => evt[:state],
:status => evt[:status],
:time => evt[:time]
}
elsif evt[:external_video_url] # Start
matched_external_video_events << {
:start_timestamp => evt[:timestamp],
:stop_timestamp => external_video_events[i + 1][:timestamp],
:external_video_url => evt[:external_video_url],
:updates => []
}
else # Stop (sometimes it occurrs without Start? -> [if e] is added)
e = matched_external_video_events[-1]
e[:stop_timestamp] = evt[:timestamp] if e
end
end
#BigBlueButton.logger.info (match_all_external_video_events: "#{matched_external_video_events}")
matched_external_video_events
end

Expand Down
4 changes: 4 additions & 0 deletions record-and-playback/presentation/scripts/presentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ deskshare_output_framerate: 5
# audio_offset = 1200 means that the audio will be delayed by 1200ms
audio_offset: 0
include_deskshare: true
# you need to directly modify /usr/local/bigbluebutton/core/scripts/presentation.yml,
# or overwrite it by /etc/bigbluebutton/recording/presentation.yml
# without this parameter, the external_videos.png file won't be generated and the thumbnail disappears in the player.
include_external_videos: true

# For PRODUCTION
publish_dir: /var/bigbluebutton/published/presentation
Expand Down
80 changes: 75 additions & 5 deletions record-and-playback/presentation/scripts/publish/presentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -838,12 +838,16 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times
end
end

# Changes must be directly applied to /usr/local/bigbluebutton/core/scripts/publish/presentation.rb
def events_get_image_info(slide, tldraw)
slide_deskshare = slide[:deskshare]
slide_external_videos = slide[:external_videos]
slide_presentation = slide[:presentation]

if slide_deskshare
slide[:src] = 'presentation/deskshare.png'
elsif slide_external_videos
slide[:src] = 'presentation/externalVideos.png'
elsif slide_presentation == ''
slide[:src] = 'presentation/logo.png'
else
Expand All @@ -862,7 +866,7 @@ def events_get_image_info(slide, tldraw)
File.write(image_path, '<svg version="1.1" width="1600" height="1600" xmlns="http://www.w3.org/2000/svg"></svg>')
else
command = \
if slide_deskshare
if slide_deskshare || slide_external_videos
['convert', '-size',
"#{@presentation_props['deskshare_output_width']}x#{@presentation_props['deskshare_output_height']}", 'xc:transparent', '-background', 'transparent', image_path,]
else
Expand Down Expand Up @@ -957,6 +961,15 @@ def process_presentation(package_dir)
slide_changed = true
end

when 'StartExternalVideoRecordEvent'
external_videos = slide_changed = true if @presentation_props['include_external_videos']

when 'StopExternalVideoRecordEvent'
if @presentation_props['include_external_videos']
external_videos = false
slide_changed = true
end

when 'AddShapeEvent', 'ModifyTextEvent'
events_parse_shape(shapes, event, current_presentation, current_slide, timestamp)

Expand Down Expand Up @@ -997,7 +1010,8 @@ def process_presentation(package_dir)
if slide &&
(slide[:presentation] == current_presentation) &&
(slide[:slide] == current_slide) &&
(slide[:deskshare] == deskshare)
(slide[:deskshare] == deskshare) &&
(slide[:external_videos] == external_videos)
BigBlueButton.logger.info('Presentation/Slide: skipping, no changes')
else
if slide
Expand All @@ -1011,6 +1025,7 @@ def process_presentation(package_dir)
slide: current_slide,
in: timestamp,
deskshare: deskshare,
external_videos: external_videos,
}
events_get_image_info(slide, tldraw)
slides << slide
Expand Down Expand Up @@ -1261,7 +1276,7 @@ def process_external_video_events(_events, package_dir)
BigBlueButton.logger.info('Processing external video events')

# Retrieve external video events
external_video_events = BigBlueButton::Events.match_start_and_stop_external_video_events(
external_video_events = BigBlueButton::Events.match_all_external_video_events(
BigBlueButton::Events.get_start_and_stop_external_video_events(@doc)
)

Expand All @@ -1270,14 +1285,17 @@ def process_external_video_events(_events, package_dir)
external_video_events.each do |event|
BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}")
start_timestamp = event[:start_timestamp]
stop_timestamp = event[:stop_timestamp]
timestamp = (translate_timestamp(start_timestamp) / 1000).to_i
# do not add same external_video twice
next if external_videos.find { |ev| ev[:timestamp] == timestamp }

re_start_timestamp = re[:start_timestamp]
re_stop_timestamp = re[:stop_timestamp]
next unless ((start_timestamp >= re_start_timestamp) && (start_timestamp <= re_stop_timestamp)) ||
((start_timestamp < re_start_timestamp) && (re_stop_timestamp >= re_start_timestamp))
next unless ((start_timestamp >= re_start_timestamp) && (start_timestamp < re_stop_timestamp)) ||
((stop_timestamp > re_start_timestamp) && (stop_timestamp <= re_stop_timestamp)) ||
((start_timestamp <= re_start_timestamp) && (stop_timestamp >= re_stop_timestamp) &&
(re_stop_timestamp > re_start_timestamp))

external_videos << {
timestamp: timestamp,
Expand All @@ -1287,6 +1305,58 @@ def process_external_video_events(_events, package_dir)
end

generate_json_file(package_dir, 'external_videos.json', external_videos)

# Generate external_videos.xml for playback video within a presentation
# See: https://github.com/bigbluebutton/bbb-playback/pull/127
# You need to directly modify the script /usr/local/bigbluebutton/core/scripts/publish/presentation.rb
external_videos_play = []
@rec_events.each do |re|
external_video_events.each do |event|
start_timestamp = event[:start_timestamp]
stop_timestamp = event[:stop_timestamp]
# do not add same external_video twice
next if external_videos_play.find { |ev| ev[:start_timestamp] == start_timestamp }

re_start_timestamp = re[:start_timestamp]
re_stop_timestamp = re[:stop_timestamp]
#next unless ((start_timestamp >= re_start_timestamp) && (start_timestamp <= re_stop_timestamp)) ||
# ((start_timestamp < re_start_timestamp || stop_timestamp > re_stop_timestamp) && (re_stop_timestamp >= re_start_timestamp))
next unless ((start_timestamp >= re_start_timestamp) && (start_timestamp < re_stop_timestamp)) ||
((stop_timestamp > re_start_timestamp) && (stop_timestamp <= re_stop_timestamp)) ||
((start_timestamp <= re_start_timestamp) && (stop_timestamp >= re_stop_timestamp) &&
(re_stop_timestamp > re_start_timestamp))

updates = []
event[:updates].each do |update|
update[:timestamp] = (translate_timestamp(update[:timestamp]) / 1000)
update[:type] = update[:status]
update.delete(:status)
update[:playing] = update[:state] == 0 ? false : true
update.delete(:state)
updates << update
end

external_videos_play << {
start_timestamp: (translate_timestamp(event[:start_timestamp]) / 1000),
stop_timestamp: (translate_timestamp(event[:stop_timestamp]) / 1000),
url: event[:external_video_url],
updates: updates
}
end
end

xml_object = Nokogiri::XML::Builder.new do |xml|
xml.recording(:id => "external_videos_events") do
external_videos_play.each do |video|
xml.video(:start_timestamp => video[:start_timestamp], :stop_timestamp => video[:stop_timestamp], :url => video[:url]) do
video[:updates].each do |update|
xml.event(update)
end
end
end
end
end
File.open("#{package_dir}/external_videos.xml", 'w') { |f| f.puts(Nokogiri::XML(xml_object.to_xml, nil, 'utf-8').to_xml) }
end

def generate_done_or_fail_file(success)
Expand Down
Loading