Skip to content

Instantly share code, notes, and snippets.

@Flamefork
Created December 27, 2011 22:21
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save Flamefork/1525347 to your computer and use it in GitHub Desktop.
Save Flamefork/1525347 to your computer and use it in GitHub Desktop.
ffmpeg & carrierwave
module CarrierWave
module FFMPEG
extend ActiveSupport::Concern
module ClassMethods
def faststart
process :faststart => true
end
def transcode options
process :transcode => options
end
def send_thumbnail_to method_name
process :send_thumbnail_to => method_name
end
end
def transcode *args
options = args.inject({}){|o, p| o[p.first] = p.last; o } # combining hash from array of pairs
options = {:custom => rotation_filters}.merge(options)
transcoder_options = {
:preserve_aspect_ratio => options.delete(:preserve_aspect_ratio),
:max_dimensions => options.delete(:max_dimensions)
}
with_temp_filepath do |temp_path|
FileUtils.mv current_path, temp_path
file = MovieWithRotationSupport.new temp_path
file.transcode current_path, options, transcoder_options
end
end
def faststart _
with_temp_filepath do |temp_path|
FileUtils.mv current_path, temp_path
puts 'Applying qt-faststart'
`qt-faststart #{temp_path} #{current_path}`
end
end
def send_thumbnail_to method_name
with_temp_filepath ['thumbnail', '.jpg'] do |temp_path|
file = MovieWithRotationSupport.new current_path
file.transcode temp_path, :custom => "-ss 00:00:01 -vframes 1 -f image2 #{rotation_filters}"
model.send method_name, File.new(temp_path)
end
end
private
def with_temp_filepath basename = 'video'
tmpfile = Tempfile.new basename
tmpfile.close
yield tmpfile.path
ensure
tmpfile.unlink
end
def rotation_filters
case MiniExiftool.new(current_path).rotation
when 180
'-vf "vflip,hflip"'
when 90
'-vf "transpose=1"'
when 270
'-vf "transpose=2"'
else
''
end
end
class MovieWithRotationSupport < ::FFMPEG::Movie
attr_reader :rotation
def initialize path
super
@rotation = MiniExiftool.new(path).rotation
@resolution = @resolution.split('x').reverse.join('x') if rotated?
end
def transcode(output_file, options = EncodingOptions.new, transcoder_options = {}, &block)
TranscoderWithMaxDimensionsSupport.new(self, output_file, options, transcoder_options).run &block
end
protected
def rotated?
@rotation % 180 != 0
end
end
class TranscoderWithMaxDimensionsSupport < ::FFMPEG::Transcoder
def apply_transcoder_options
super
return if @movie.calculated_aspect_ratio.nil?
if @transcoder_options[:max_dimensions]
new_height = @movie.height
new_width = @movie.width
if new_width > @raw_options.width
new_width = @raw_options.width
new_height = new_width / @movie.calculated_aspect_ratio
new_height = new_height.ceil.even? ? new_height.ceil : new_height.floor
new_height += 1 if new_height.odd? # needed if new_height ended up with no decimals in the first place
end
if new_height > @raw_options.height
new_height = @raw_options.height
new_width = new_height * @movie.calculated_aspect_ratio
new_width = new_width.ceil.even? ? new_width.ceil : new_width.floor
new_width += 1 if new_width.odd?
end
if @raw_options[:custom] && @raw_options[:custom]['transpose']
@raw_options[:resolution] = "#{new_height}x#{new_width}"
else
@raw_options[:resolution] = "#{new_width}x#{new_height}"
end
@raw_options[:aspect] = new_width.to_f / new_height.to_f
end
end
end
end
end
class VideoUploader < CarrierWave::Uploader::Base
include CarrierWave::MimeTypes
include CarrierWave::FFMPEG
storage :fog
def store_dir
"uploads/#{model.class.table_name}/#{mounted_as}/#{model.id}"
end
process :set_content_type
version :default do
process :send_thumbnail_to => :photo=,
:transcode => {
:audio_codec => 'copy',
:video_codec => 'libx264',
:video_bitrate => 500,
:resolution => '640x640',
:max_dimensions => true
},
:faststart => true
end
def extension_white_list
%w(mov mp4 mpe mpg m4v)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment