所以昨晚很长一段时间我都在努力解决这个问题,最后想出来,所以我想在这里发帖,以防有人遇到同样的问题。
目标是解析FFmpeg的输出,使其在Sidekiq工作器中运行并将进度和持续时间保存到ActiveRecord模型,这样我就可以通过轮询数据库在UI中获得进度条。
如何在不等待流程完成的情况下实时解析ffmpeg duration
和time
?
答案 0 :(得分:1)
有两个问题:
对于第一个,我查看了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