9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
# File 'lib/shellfie/ffmpeg_encoder.rb', line 9
def self.encode(images, output_path, format:, command:, framerate:, playback_speed:, loop:, loop_count: nil,
apng_prediction: nil)
list = Tempfile.new(["shellfie-frames", ".txt"])
minimum_delay = 1_000.0 / framerate
images.each do |image|
list.puts "file '#{image[:path].gsub("'", "'\\''")}'"
list.puts "duration #{[image[:delay].to_f, minimum_delay].max / 1_000.0 / playback_speed}"
end
list.puts "file '#{images.last[:path].gsub("'", "'\\''")}'"
list.close
total_duration = images.sum { |image| [image[:delay].to_f, minimum_delay].max } / 1_000.0 / playback_speed
filters = ["fps=#{framerate}"]
codec = case format
when "mp4"
filters << "pad=ceil(iw/2)*2:ceil(ih/2)*2"
%w[-c:v libx264 -pix_fmt yuv420p -movflags +faststart]
when "webm"
filters << "pad=ceil(iw/2)*2:ceil(ih/2)*2"
%w[-c:v libvpx-vp9 -pix_fmt yuva420p]
when "apng"
filters << "format=rgba"
options = ["-plays", (loop_count || (loop ? 0 : 1)).to_s]
options.concat(["-pred", apng_prediction]) if apng_prediction
options.concat(["-f", "apng"])
else raise RenderError, "Unsupported ffmpeg format: #{format}"
end
timing = ["-vf", filters.join(","), "-fps_mode", "cfr", "-t", total_duration.to_s]
_stdout, stderr, status = Open3.capture3(command, "-y", "-f", "concat", "-safe", "0", "-i", list.path,
*timing, *codec, output_path)
raise RenderError, "ffmpeg encode failed: #{stderr}" unless status.success?
output_path
ensure
list&.close!
end
|