如何在ruby中实时解析ffmpeg进度

时间:2015-10-07 18:33:13

标签: ruby-on-rails ruby ffmpeg popen

所以昨晚很长一段时间我都在努力解决这个问题,最后想出来,所以我想在这里发帖,以防有人遇到同样的问题。

目标是解析FFmpeg的输出,使其在Sidekiq工作器中运行并将进度和持续时间保存到ActiveRecord模型,这样我就可以通过轮询数据库在UI中获得进度条。

如何在不等待流程完成的情况下实时解析ffmpeg durationtime

1 个答案:

答案 0 :(得分:1)

有两个问题:

  1. 如何在ruby中实时解析输出?
  2. 由于FFmpeg将进度输出写入一行,您如何获得该输出?
  3. 对于第一个,我查看了ruby Open3模块,this article特别有帮助。要点是:

    require 'open3'
    cmd = "bash my_long_running_command.sh"
    Open3.popen3(cmd) do |stdin, stdout, stderr, thread|
      stdout.each do |line|
        # this will print as stdout is being written to the stream real-time
        puts line
      end
    end
    

    现在这适用于许多命令,但不适用于FFmpeg。 FFmpeg很奇怪,因为它将进度和输出写入STDERR而不是STDOUT

    不仅如此,但我最终注意到FFmpeg的行分隔符不是\n,因为它在大多数bash命令中,而是\r,这是它能够如何编辑内联进度。现在我们知道唯一的更新是流和分隔符,可以在each方法中作为第一个参数覆盖。

    require 'open3'
    cmd = "ffmpeg -i input.mp4 ... output.mp4"
    Open3.popen3(cmd) do |stdin, stdout, stderr, thread|
      duration = 0
      progress = 0
      stderr.each("\r") do |line|
        next unless (line.include?("time=") || line.include?("Duration:"))
        if duration == 0
          if line =~ /Duration:\s+(\d{2}):(\d{2}):(\d{2}).(\d{1,2})/
            duration = $1.to_i * 3600 + $2.to_i * 60 + $3
          end
        end 
    
        percentage = if line =~ /time(\d{2}):(\d{2}):(\d{2})/
          progress = $1.to_i * 3600 + $2.to_i * 60 + $3
          (progress.to_f/duration.to_f) * 100
        end
    
        puts "#{percentage}%"
      end
    end
    

    你也可以使用我发现的一个名为stremio-ffmpeg的非常相似的宝石,在我已经实现了我的解决方案后,我很遗憾地发现了这个宝石。如果你想做同样的事情,那就做:

    require 'streamio-ffmpeg'
    movie = FFMPEG::Movie.new('input.mp4')
    duration = movie.duration
    movie.transcode('output.mp4', options_as_a_string) do |progress|
      percentage = (progress.to_f/duration.to_f) * 100
      puts "#{percentage}%"
    end