Last active
October 4, 2020 14:40
-
-
Save galanin/6616965426eaf5a6221f0fff7ac1db8f to your computer and use it in GitHub Desktop.
Пример длинного метода из https://github.com/bigbluebutton/bigbluebutton/blob/dba5cd9196249aa72bb19666ba232f4ac5211579/record-and-playback/core/lib/recordandplayback/edl/video.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def self.composite_cut(output, cut, layout, videoinfo) | |
duration = cut[:next_timestamp] - cut[:timestamp] | |
BigBlueButton.logger.info " Cut start time #{cut[:timestamp]}, duration #{duration}" | |
ffmpeg_filter = "color=c=white:s=#{layout[:width]}x#{layout[:height]}:r=24" | |
layout[:areas].each do |layout_area| | |
area = cut[:areas][layout_area[:name]] | |
video_count = area.length | |
BigBlueButton.logger.debug " Laying out #{video_count} videos in #{layout_area[:name]}" | |
next if video_count == 0 | |
tile_offset_x = layout_area[:x] | |
tile_offset_y = layout_area[:y] | |
tiles_h = 0 | |
tiles_v = 0 | |
tile_width = 0 | |
tile_height = 0 | |
total_area = 0 | |
# Do an exhaustive search to maximize video areas | |
for tmp_tiles_v in 1..video_count | |
tmp_tiles_h = (video_count / tmp_tiles_v.to_f).ceil | |
tmp_tile_width = (2 * (layout_area[:width].to_f / tmp_tiles_h / 2).floor).to_i | |
tmp_tile_height = (2 * (layout_area[:height].to_f / tmp_tiles_v / 2).floor).to_i | |
next if tmp_tile_width <= 0 or tmp_tile_height <= 0 | |
tmp_total_area = 0 | |
area.each do |video| | |
video_width = videoinfo[video[:filename]][:width] | |
video_height = videoinfo[video[:filename]][:height] | |
scale_width, scale_height = aspect_scale(video_width, video_height, tmp_tile_width, tmp_tile_height) | |
tmp_total_area += scale_width * scale_height | |
end | |
if tmp_total_area > total_area | |
tiles_h = tmp_tiles_h | |
tiles_v = tmp_tiles_v | |
tile_width = tmp_tile_width | |
tile_height = tmp_tile_height | |
total_area = tmp_total_area | |
end | |
end | |
tile_x = 0 | |
tile_y = 0 | |
BigBlueButton.logger.debug " Tiling in a #{tiles_h}x#{tiles_v} grid" | |
ffmpeg_filter << "[#{layout_area[:name]}_in];" | |
area.each do |video| | |
BigBlueButton.logger.debug " tile location (#{tile_x}, #{tile_y})" | |
video_width = videoinfo[video[:filename]][:width] | |
video_height = videoinfo[video[:filename]][:height] | |
BigBlueButton.logger.debug " original size: #{video_width}x#{video_height}" | |
scale_width, scale_height = aspect_scale(video_width, video_height, tile_width, tile_height) | |
BigBlueButton.logger.debug " scaled size: #{scale_width}x#{scale_height}" | |
offset_x, offset_y = pad_offset(scale_width, scale_height, tile_width, tile_height) | |
BigBlueButton.logger.debug " offset: left: #{offset_x}, top: #{offset_y}" | |
BigBlueButton.logger.debug " start timestamp: #{video[:timestamp]}" | |
BigBlueButton.logger.debug(" codec: #{videoinfo[video[:filename]][:video][:codec_name].inspect}") | |
BigBlueButton.logger.debug(" duration: #{videoinfo[video[:filename]][:duration]}, original duration: #{video[:original_duration]}") | |
if videoinfo[video[:filename]][:video][:codec_name] == "flashsv2" | |
# Desktop sharing videos in flashsv2 do not have regular | |
# keyframes, so seeking in them doesn't really work. | |
# To make processing more reliable, always decode them from the | |
# start in each cut. (Slow!) | |
seek = 0 | |
else | |
# Webcam videos are variable, low fps; it might be that there's | |
# no frame until some time after the seek point. Start decoding | |
# 30s before the desired point to avoid this issue. | |
seek = video[:timestamp] - 30000 | |
seek = 0 if seek < 0 | |
end | |
# Workaround early 1.1 deskshare timestamp bug | |
# It resulted in video files that were too short. To workaround, we | |
# assume that the framerate was constant throughout (it might not | |
# actually be...) and scale the video length. | |
scale = nil | |
if !video[:original_duration].nil? and | |
videoinfo[video[:filename]][:video][:deskshare_timestamp_bug] | |
scale = video[:original_duration].to_f / videoinfo[video[:filename]][:duration] | |
# Rather than attempt to recalculate seek... | |
seek = 0 | |
BigBlueButton.logger.debug(" Early 1.1 deskshare timestamp bug: scaling video length by #{scale}") | |
end | |
pad_name = "#{layout_area[:name]}_x#{tile_x}_y#{tile_y}" | |
ffmpeg_filter << "movie=#{video[:filename]}:sp=#{ms_to_s(seek)}" | |
if !scale.nil? | |
ffmpeg_filter << ",setpts=PTS*#{scale}" | |
end | |
ffmpeg_filter << ",fps=#{FFMPEG_WF_FRAMERATE}:start_time=#{ms_to_s(video[:timestamp])}" | |
ffmpeg_filter << ",setpts=PTS-STARTPTS,scale=#{scale_width}:#{scale_height}" | |
ffmpeg_filter << ",pad=w=#{tile_width}:h=#{tile_height}:x=#{offset_x}:y=#{offset_y}:color=white" | |
ffmpeg_filter << "[#{pad_name}];" | |
tile_x += 1 | |
if tile_x >= tiles_h | |
tile_x = 0 | |
tile_y += 1 | |
end | |
end | |
remaining = video_count | |
(0...tiles_v).each do |tile_y| | |
this_tiles_h = [tiles_h, remaining].min | |
remaining -= this_tiles_h | |
(0...this_tiles_h).each do |tile_x| | |
ffmpeg_filter << "[#{layout_area[:name]}_x#{tile_x}_y#{tile_y}]" | |
end | |
if this_tiles_h > 1 | |
ffmpeg_filter << "hstack=inputs=#{this_tiles_h}," | |
end | |
ffmpeg_filter << "pad=w=#{layout_area[:width]}:h=#{tile_height}:color=white" | |
ffmpeg_filter << "[#{layout_area[:name]}_y#{tile_y}];" | |
end | |
(0...tiles_v).each do |tile_y| | |
ffmpeg_filter << "[#{layout_area[:name]}_y#{tile_y}]" | |
end | |
if tiles_v > 1 | |
ffmpeg_filter << "vstack=inputs=#{tiles_v}," | |
end | |
ffmpeg_filter << "pad=w=#{layout_area[:width]}:h=#{layout_area[:height]}:color=white" | |
ffmpeg_filter << "[#{layout_area[:name]}];" | |
ffmpeg_filter << "[#{layout_area[:name]}_in][#{layout_area[:name]}]overlay=x=#{layout_area[:x]}:y=#{layout_area[:y]}" | |
end | |
ffmpeg_filter << ",trim=end=#{ms_to_s(duration)}" | |
ffmpeg_cmd = [*FFMPEG] | |
ffmpeg_cmd += ['-filter_complex', ffmpeg_filter, *FFMPEG_WF_ARGS, '-'] | |
File.open(output, 'a') do |outio| | |
exitstatus = BigBlueButton.exec_redirect_ret(outio, *ffmpeg_cmd) | |
raise "ffmpeg failed, exit code #{exitstatus}" if exitstatus != 0 | |
end | |
return output | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment