# # FFmpeg Abstraction # require 'capturer' require 'command' require 'time_index' module VCSRuby class FFmpeg < Capturer CODEC = 3 DIMENSION = 5 FPS = 7 def initialize video @video = video @ffmpeg = Command.new :ffmpeg, 'ffmpeg' @ffprobe = Command.new :ffmpeg, 'ffprobe' detect_version end def name :ffmpeg end def available? @ffmpeg.available? && !libav? end def libav? @libav end def detect_version info = @ffmpeg.execute('-version') match = /avconv ([\d|.|-|:]*)/.match(info) @libav = true if match match = /ffmpeg version ([\d|.]*)/.match(info) if match @version = match[1] end end def length load_probe match = /Duration: ([\d|:|.]*)/.match(@cache) return TimeIndex.new match[1] end def width load_probe @width end def height load_probe @height end def par load_probe @par end def dar load_probe @dar end def fps load_probe @fps end def video_codec load_probe @video_codec end def audio_codec load_probe @audio_codec end def grab time, image_path @ffmpeg.execute "-y -ss #{time.total_seconds} -i \"#{@video}\" -an -dframes 1 -vframes 1 -vcodec png -f rawvideo \"#{image_path}\"" end def to_s "FFmpeg #{@version}" end private def load_probe return if @cache @cache = @ffprobe.execute("\"#{@video}\"", "2>&1") puts @cache if Tools.verbose? parse_video_streams parse_audio_streams end def parse_video_streams video_stream = split_stream_line(is_stream?(@cache, /Video/).first) dimensions = /(\d*)x(\d*) \[PAR (\d*:\d*) DAR (\d*:\d*)\]/.match(video_stream[DIMENSION]) if dimensions @par = dimensions[3] @dar = dimensions[4] else dimensions = /(\d*)x(\d*)/.match(video_stream[DIMENSION]) end if dimensions @width = dimensions[1].to_i @height = dimensions[2].to_i end fps = /([\d|.]+) fps/.match(video_stream[FPS]) @fps = fps ? fps[1].to_f : 0.0 @video_codec = video_stream[CODEC] end def parse_audio_streams audio_stream = split_stream_line(is_stream?(@cache, /Audio/).first) @audio_codec = audio_stream[CODEC] end def is_stream? probe, regex streams(probe).select{ |s| s =~ regex } end def streams probe @cache.split(/\r?\n/).map(&:strip).select{|l| l.start_with? 'Stream' } end def split_stream_line line parts = line.split(',') stream = parts.shift result = stream.split(':') result += parts return result.map(&:strip) end end end