class Paperclip::Ffmpeg
Attributes
convert_options[RW]
format[RW]
geometry[RW]
whiny[RW]
Public Class Methods
detect_command(command)
click to toggle source
# File lib/paperclip_processors/ffmpeg.rb, line 246 def self.detect_command(command) command = "if command -v #{command} 2>/dev/null; then echo \"true\"; else echo \"false\"; fi" Ffmpeg.log(command) if @whiny result = Cocaine::CommandLine.new(command).run Ffmpeg.log("Result of command: #{result}") if @whiny case result when /true/ return true when /false/ return false else return nil end end
detect_ffmpeg_or_avconv()
click to toggle source
# File lib/paperclip_processors/ffmpeg.rb, line 216 def self.detect_ffmpeg_or_avconv # Check whether ffmpeg or avconv is installed result = Ffmpeg.detect_command("ffmpeg") Ffmpeg.log("Result of command: #{result}") if @whiny if result == true Ffmpeg.log("Result of command: #{"ffmpeg"}") if @whiny return "ffmpeg" elsif result == false Ffmpeg.log("Result of command: #{"avconv"}") if @whiny return "avconv" else return "Error: no video conversion library detected. Please install ffmpeg or avconv." end end
detect_ffprobe_or_avprobe()
click to toggle source
# File lib/paperclip_processors/ffmpeg.rb, line 231 def self.detect_ffprobe_or_avprobe # Check whether ffprobe or avprobe is installed result = Ffmpeg.detect_command("ffprobe") Ffmpeg.log("Result of command: #{result}") if @whiny if result == true Ffmpeg.log("Result of command: #{"ffprobe"}") if @whiny return "ffprobe" elsif result == false Ffmpeg.log("Result of command: #{"avprobe"}") if @whiny return "avprobe" else return "Error: no video conversion library detected. Please install ffmpeg or avconv." end end
log(message)
click to toggle source
# File lib/paperclip_processors/ffmpeg.rb, line 212 def self.log message Paperclip.log "[ffmpeg] #{message}" end
new(file, options = {})
click to toggle source
Creates a Video object set to work on the file
given. It will attempt to transcode the video into one defined by target_geometry
which is a “WxH”-style string. format
should be specified. Video transcoding will raise no errors unless whiny
is true (which it is, by default. If convert_options
is set, the options will be appended to the convert command upon video transcoding.
# File lib/paperclip_processors/ffmpeg.rb, line 11 def initialize file, options = {}, attachment = nil @convert_options = { :input => {}, :output => { :y => nil } } unless options[:convert_options].nil? || options[:convert_options].class != Hash unless options[:convert_options][:input].nil? || options[:convert_options][:input].class != Hash @convert_options[:input].reverse_merge! options[:convert_options][:input] end unless options[:convert_options][:output].nil? || options[:convert_options][:output].class != Hash @convert_options[:output].reverse_merge! options[:convert_options][:output] end end @geometry = options[:geometry] @file = file @keep_aspect = !@geometry.nil? && @geometry[-1,1] != '!' @pad_only = @keep_aspect && @geometry[-1,1] == '#' @enlarge_only = @keep_aspect && @geometry[-1,1] == '<' @shrink_only = @keep_aspect && @geometry[-1,1] == '>' @whiny = options[:whiny].nil? ? true : options[:whiny] @format = options[:format] @time = options[:time].nil? ? 3 : options[:time] @current_format = File.extname(@file.path) @basename = File.basename(@file.path, @current_format) @meta = identify @pad_color = options[:pad_color].nil? ? "black" : options[:pad_color] @auto_rotate = options[:auto_rotate].nil? ? false : options[:auto_rotate] attachment.instance_write(:meta, @meta) end
Public Instance Methods
identify()
click to toggle source
# File lib/paperclip_processors/ffmpeg.rb, line 181 def identify meta = {} av_lib_version = Ffmpeg.detect_ffprobe_or_avprobe command = "#{av_lib_version} \"#{File.expand_path(@file.path)}\" 2>&1" Paperclip.log("[ffmpeg] #{command}") ffmpeg = Cocaine::CommandLine.new(command).run ffmpeg.split("\n").each do |line| if line =~ /(([\d\.]*)\s.?)fps,/ meta[:fps] = $1.to_i end # Matching lines like: # Video: h264, yuvj420p, 640x480 [PAR 72:72 DAR 4:3], 10301 kb/s, 30 fps, 30 tbr, 600 tbn, 600 tbc if line =~ /Video:(.*)/ v = $1.to_s size = v.match(/\d{3,5}x\d{3,5}/).to_s meta[:size] = size meta[:aspect] = size.split('x').first.to_f / size.split('x').last.to_f end # Matching Duration: 00:01:31.66, start: 0.000000, bitrate: 10404 kb/s if line =~ /Duration:(\s.?(\d*):(\d*):(\d*\.\d*))/ meta[:length] = $2.to_s + ":" + $3.to_s + ":" + $4.to_s end if line =~ /rotate\s*:\s(\d*)/ meta[:rotate] = $1.to_i end end Paperclip.log("[ffmpeg] Command Success") if @whiny meta end
make()
click to toggle source
Performs the transcoding of the file
into a thumbnail/video. Returns the Tempfile that contains the new image/video.
# File lib/paperclip_processors/ffmpeg.rb, line 43 def make Ffmpeg.log("Making...") if @whiny src = @file Ffmpeg.log("Building Destination File: '#{@basename}' + '#{@format}'") if @whiny dst = Tempfile.new([@basename, @format ? ".#{@format}" : '']) Ffmpeg.log("Destination File Built") if @whiny dst.binmode parameters = [] Ffmpeg.log("Adding Geometry") if @whiny # Add geometry if @geometry Ffmpeg.log("Extracting Target Dimensions") if @whiny # Extract target dimensions if @geometry =~ /(\d*)x(\d*)/ target_width = $1 target_height = $2 end # Only calculate target dimensions if we have current dimensions unless @meta[:size].nil? Ffmpeg.log("Target Size is Available") if @whiny current_width, current_height = @meta[:size].split('x') # Current width and height if @keep_aspect Ffmpeg.log("Keeping Aspect Ratio") if @whiny if @enlarge_only Ffmpeg.log("Enlarge Only") if @whiny if current_width.to_i < target_width.to_i # Keep aspect ratio width = target_width.to_i height = (width.to_f / (@meta[:aspect].to_f)).to_i @convert_options[:output][:s] = "#{width.to_i/2*2}x#{height.to_i/2*2}" Ffmpeg.log("Convert Options: #{@convert_options[:output][:s]}") if @whiny else Ffmpeg.log("Source is Larger than Destination, Doing Nothing") if @whiny #return nil end elsif @shrink_only Ffmpeg.log("Shrink Only") if @whiny if current_width.to_i > target_width.to_i # Keep aspect ratio width = target_width.to_i height = (width.to_f / (@meta[:aspect].to_f)).to_i @convert_options[:output][:s] = "#{width.to_i/2*2}x#{height.to_i/2*2}" Ffmpeg.log("Convert Options: #{@convert_options[:output][:s]}") if @whiny else Ffmpeg.log("Source is Smaller than Destination, Doing Nothing") if @whiny #return nil end elsif @pad_only Ffmpeg.log("Pad Only") if @whiny # Keep aspect ratio width = target_width.to_i height = (width.to_f / (@meta[:aspect].to_f)).to_i # We should add half the delta as a padding offset Y pad_y = (target_height.to_f - height.to_f) / 2 # There could be options already set @convert_options[:output][:vf][/\A/] = ',' if @convert_options[:output][:vf] @convert_options[:output][:vf] ||= '' if pad_y > 0 @convert_options[:output][:vf][/\A/] = "scale=#{width}:-1,pad=#{width.to_i}:#{target_height.to_i}:0:#{pad_y}:#@pad_color" else @convert_options[:output][:vf][/\A/] = "scale=#{width}:-1,crop=#{width.to_i}:#{height.to_i}" end Ffmpeg.log("Convert Options: #{@convert_options[:output][:s]}") if @whiny else Ffmpeg.log("Resize") if @whiny # Keep aspect ratio width = target_width.to_i height = (width.to_f / (@meta[:aspect].to_f)).to_i @convert_options[:output][:s] = "#{width.to_i/2*2}x#{height.to_i/2*2}" Ffmpeg.log("Convert Options: #{@convert_options[:output][:s]}") if @whiny end else Ffmpeg.log("Not Keeping Aspect Ratio") if @whiny # Do not keep aspect ratio @convert_options[:output][:s] = "#{target_width.to_i/2*2}x#{target_height.to_i/2*2}" Ffmpeg.log("Convert Options: #{@convert_options[:output][:s]}") if @whiny end end end # If file has rotation, rotate it back to horizontal if @auto_rotate && !@meta[:rotate].nil? Ffmpeg.log("Adding rotation #{@meta[:rotate]}") if @whiny case @meta[:rotate] when 90 @convert_options[:output][:vf] = 'transpose=1' when 180 @convert_options[:output][:vf] = 'vflip, hflip' when 270 @convert_options[:output][:vf] = 'transpose=2' end end Ffmpeg.log("Adding Format") if @whiny # Add format case @format when 'jpg', 'jpeg', 'png', 'gif' # Images @convert_options[:input][:ss] = @time @convert_options[:output][:vframes] = 1 @convert_options[:output][:f] = 'image2' when 'webm' # WebM @convert_options[:output][:acodec] = 'libvorbis' @convert_options[:output][:vcodec] = 'libvpx' @convert_options[:output][:f] = 'webm' when 'ogv' # Ogg Theora @convert_options[:output][:acodec] = 'libvorbis' @convert_options[:output][:vcodec] = 'libtheora' @convert_options[:output][:f] = 'ogg' when 'mp4' @convert_options[:output][:acodec] = 'aac' @convert_options[:output][:strict] = 'experimental' end Ffmpeg.log("Adding Source") if @whiny # Add source # Validations on the values. These could be either nil. parameters << @convert_options[:input].map { |k,v| "-#{k.to_s} #{v} " if !v.nil? && (v.is_a?(Numeric) || !v.empty?) } parameters << "-i :source" parameters << @convert_options[:output].map { |k,v| "-#{k.to_s} #{v} " if !v.nil? && (v.is_a?(Numeric) || !v.empty?) } parameters << "-y :dest" Ffmpeg.log("Building Parameters") if @whiny parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ") Ffmpeg.log(parameters) begin av_lib_version = Ffmpeg.detect_ffmpeg_or_avconv success = Paperclip.run(av_lib_version, parameters, :source => "#{File.expand_path(src.path)}", :dest => File.expand_path(dst.path)) rescue Cocaine::ExitStatusError => e raise Paperclip::Error, "error while processing video for #{@basename}: #{e}" if @whiny end dst end