Skip to content

Instantly share code, notes, and snippets.

@Paxa
Created September 8, 2011 03:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Paxa/1202521 to your computer and use it in GitHub Desktop.
Save Paxa/1202521 to your computer and use it in GitHub Desktop.
video encoder snippet
class Attachment < ActiveRecord::Base
belongs_to :post
mount_uploader :filename, PostAttachmentUploader
VIDEO_FORMATS = %W{ogv mp4 webm}
PREVIEW_FORMAT = "jpeg"
before_save :set_metas
after_create :start_encoding!, :if => :video?
def display_name
filename.to_s.split("/").last
end
def start_encoding!
VideoEncoder.process_async(self)
end
def force_encode(format)
VideoEncoder.new(filename.path).encode_to(format.to_s, path_for(format))
end
def video?
view_as == "video"
end
def video_thumbnail
return "" unless video_ready?
url_for(PREVIEW_FORMAT)
end
def check_if_video_done
missing = []
(VIDEO_FORMATS + [PREVIEW_FORMAT]).each do |format|
missing << path_for(format) unless File.exist?(path_for(format))
end
if missing.empty?
update_attribute(:video_ready, 1)
end
missing
end
def filename_for(format)
fname = File.basename(filename.to_s, File.extname(filename.to_s))
"#{fname}_converted.#{format}"
end
def path_for(format)
fname = File.basename(filename.to_s)
filename.path.chomp(fname) + filename_for(format)
end
def url_for(format)
fname = File.basename(filename.to_s)
filename.url.chomp(fname) + filename_for(format)
end
def set_metas
self.size = File.size(filename.path)
extname = File.extname(filename.path).downcase
self.view_as =
if %W{.png .jpg .jpeg .gif .bmp .tiff}.include?(extname)
"picture"
elsif %W{.doc .docx .pdf .rtf .txt}.include?(extname)
"document"
elsif %W{.avi .mp4 .ogv .webm .mpg .mpeg .flv .mkv .wmv }.include?(extname)
"video"
else
"other"
end
end
end
class Jobs::Encoder
def initialize(id, format)
@id = id
@format = format
end
def perform
attachment = Attachment.find(@id)
VideoEncoder.new(attachment.filename.path).encode_to(@format, attachment.path_for(@format))
attachment.check_if_video_done
end
end
# encoding: utf-8
class PostAttachmentUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"system/#{model.class.to_s.underscore}/#{model.id}"
end
end
class VideoEncoder
WIDTH = 480
HEIGHT = 320
def self.process_async(attachment)
(attachment.class::VIDEO_FORMATS + [attachment.class::PREVIEW_FORMAT]).each do |format|
Delayed::Job.enqueue Jobs::Encoder.new(attachment.id, format)
end
end
def initialize(filename)
@filename = filename
end
def encode_to(format, output_file)
case format
when "ogv" then encode_ogv(output_file)
when "mp4" then encode_mp4(output_file)
when "webm" then encode_webm(output_file)
when "jpeg" then encode_jpeg(output_file)
end
end
def encode_ogv(output_file)
delete_old_file(output_file)
command = %{ffmpeg2theora --videoquality 5 --audioquality 1 --max_size #{ratio} \
--output #{output_file} #{@filename}}
run_cmd(command)
end
def encode_mp4(output_file)
delete_old_file(output_file)
command = %{HandBrakeCLI --preset "iPhone & iPod Touch" --width #{WIDTH} --vb 600 --two-pass \
--input #{@filename} --output #{output_file}}
run_cmd(command)
end
def encode_webm(output_file)
delete_old_file(output_file)
delete_old_file(output_file + "-0.log")
delete_old_file(output_file + "-tmp.webm")
command1 = %{ffmpeg -pass 1 -passlogfile #{@filename} -threads 16 -keyint_min 0 -g 250 \
-skip_threshold 0 -qmin 1 -qmax 51 -i #{@filename} -vcodec libvpx -s #{ratio} \
-aspect 4:3 -an -y #{output_file}-tmp.webm}
run_cmd(command1)
command2 = %{ffmpeg -pass 2 -passlogfile #{@filename} -threads 16 -keyint_min 0 -g 250 \
-skip_threshold 0 -qmin 1 -qmax 51 -i #{@filename} -vcodec libvpx -b 614400 -s #{ratio} \
-aspect 4:3 -acodec libvorbis -ar 44100 -ab 64k -y #{output_file}}
run_cmd(command2)
run_cmd("rm #{output_file}-tmp.webm")
end
def encode_jpeg(output_file)
delete_old_file(output_file)
begin
info = run_cmd("ffmpeg -i #{@filename} -f null /dev/null 2>&1")
duration_line = info.match(/Duration:\s+([0-9:\.]+)/)
duration = duration_line[1].split(":").last.to_f
center = (duration / 2).round
rescue => e
puts e.message
center = 1
end
command = %{ffmpeg -itsoffset -#{center} -i #{@filename} -vcodec mjpeg -vframes 1 -an -f rawvideo \
-s #{ratio} #{output_file}}
run_cmd(command)
end
def run_cmd(command)
puts command
system(command)
end
def delete_old_file(file)
run_cmd("rm #{file}") if File.exist?(file)
end
def ratio
"#{WIDTH}x#{HEIGHT}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment