Skip to content

Instantly share code, notes, and snippets.

@Bertg
Created August 4, 2010 07:46
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save Bertg/507804 to your computer and use it in GitHub Desktop.
Save Bertg/507804 to your computer and use it in GitHub Desktop.
These are some snips to help you converting video to flash and thumbnails using ruby and paperclip.
Needed: ffmpeg & flvtool, RVideo gem, paperclip
Converting the video is done using outside of paperclip, as this could take quite a while. Paperclip by default does it's conversions during the request, and the last thing you want is a request hanging for 10minutes while ffmpeg does it's thing. It is advised to do this conversion somewhere in the background using Resque or DelayedJob. (make sure not to run to many conversions at one time on your machine)
Most video thumbnailing solutions out there will create all thumbnails from the video. This solution (on paperclip 2.3.0) however will create one large "temp_thumbnail" once and all consequent thumbnails can use this temporary version.
VideoGeometry will make sure that the target dimensions for ffmpeg are always a multiple of 2 and as close as possible to the desired dimensions without breaking the aspect ratio of the original video.
The video.rb file is a cleaned up example of the video file we use.
Note that this code is "cleaned up". In reality you might want to have your binaries defined somewhat more dynamically and test if the conversion steps succeeded. system() will output false if not, or use $?.exitstatus.
This code is provided as is, and is free (as in beer and ideology) to use. If you explode your server or any other negative effect arises from using this code, I can not be held responsible for it.
module Paperclip
class VideoGeometry < Geometry
attr_accessor :video
cattr_accessor :ffmpeg_binary
@@ffmpeg_binary ||= '/usr/bin/ffmpeg'
def initialize file = nil
super(file.width, file.height)
@video = file
end
def self.from_file file
file = file.path if file.respond_to? "path"
begin
file = RVideo::Inspector.new(:file => file, :ffmpeg_binary => VideoGeometry.ffmpeg_binary)
rescue Exception => exc
raise "File could not be inspected #{exc.message}"
end
parse(file)
end
def self.parse file
VideoGeometry.new(file)
end
def transformation_to dst, crop = false
warn("Don't know how to crop videos") if crop
ratio = Geometry.new( dst.width / self.width, dst.height / self.height )
scale_geometry, scale = scaling(dst, ratio)
scale_geometry
end
def scaling dst, ratio, force = false
ratio = if ratio.vertical? then ratio.width else ratio.height end
dimensions = if ratio < 1 || force
[(self.width * ratio), (self.height * ratio)]
else
[self.width, self.height]
end
dimensions.collect{|i| to_nearest_factor_of_two(i)}.join('x')
end
private
def to_nearest_factor_of_two(dimention)
a, b = dimention.ceil, dimention.floor
if b % 2 == 0
b
elsif a % 2 == 0
a
else
a + 1
end
end
end
end
class Video
has_attached_file :file,
:styles => Application.image_sizes,
:path => ":upload_path/videos/:model_id_attachment_path",
:url => ":owner_url/videos/:id/:style.:content_type_extension",
:processors => lambda { |a| Paperclip.content_types(:video).include?(a.file.content_type) ? [ :video_thumbnail ] : [ :thumbnail ] }
def self.default_dimension
'438x356'
end
def self.flvtool2_binary
'/usr/bin/flvtool2'
end
def convert!
flv_file_path = File.join(File.dirname(file.path)
dimentions = Paperclip::VideoGeometry.from_file(@original_file_path).transformation_to(Paperclip::Geometry.parse(Video.default_dimension))
system(convert_command(file.path, flv_file_path, dimentions))
system(flvtool_command(flv_file_path))
end
def convert_command(original_file_path, flv_file_path, dimentions), "#{file_file_name.gsub(/#{File.extname(self.file.original_filename)}$/, "")}.sd.flv")
%(#{Paperclip::VideoGeometry.ffmpeg_binary} -i #{original_file_path} -b 1000000 -ar 22050 -ab 64 -f flv -y -s #{dimentions}
#{flv_file_path}).gsub!(/\s+/, " ")
end
def flvtool_command(flv_file_path)
%(#{Video.flvtool2_binary} -U '#{flv_file_path}').gsub!(/\s+/, " ")
end
end
module Paperclip
class VideoThumbnail < Thumbnail
attr_accessor :time_offset, :geometry, :whiny
def self.make file, options = {}, attachment = nil
if attachment.instance.try(:temp_thumbnail) && File.exists?(attachment.instance.temp_thumbnail.path)
Paperclip.log("Found temporary thumbnail form video")
else
Paperclip.log("Building temporary thumbnail form video")
time_offset = options[:time_offset] || '-4'
dst = Tempfile.new([ @basename, 'jpg' ].compact.join("."))
dst.binmode
cmd = %Q[-itsoffset #{time_offset} -i "#{File.expand_path(file.path)}" -y -vcodec mjpeg -vframes 1 -an -f rawvideo ]
#
# Can not us the inspector on the video attachemnt. It is trying to access the saved path, but that could not be available!
# So let us use the file givven, and use our own inspector
#
inspector = RVideo::Inspector.new(:file => file.path, :ffmpeg_binary => aperclip::VideoGeometry.ffmpeg_binary) if !file.path.nil?
cmd << "-s #{inspector.resolution} "
cmd << %Q["#{File.expand_path(dst.path)}"]
begin
success = Paperclip.run('ffmpeg', cmd)
rescue PaperclipCommandLineError
whiny = options[:whiny].nil? ? true : options[:whiny] # copied form Thumbnail initializer
raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if whiny
end
attachment.instance.temp_thumbnail = dst
attachment.instance.temp_thumbnail.try(:close)
end
new(attachment.instance.temp_thumbnail, options, attachment).make
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment