Class: Shellfie::FfmpegEncoder

Inherits:
Object
  • Object
show all
Defined in:
lib/shellfie/ffmpeg_encoder.rb

Class Method Summary collapse

Class Method Details

.encode(images, output_path, format:, command:, framerate:, playback_speed:, loop:, loop_count: nil, apng_prediction: nil) ⇒ Object



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