Create FFMPEG processor for Carrierwave in Rails 3
I have had the pleasure of working with the carrierwave gem recently (as opposed to paperclip), and I must say, I am quite the fan. Once major thing I missed however, was the available list of custom user plugins for it, unlike paperclip. I believe this is mostly due to how new and recent carrierwave is. That being said, I put together a simple example of a FFMPEG process that will allow me to resample the bitrate of a file. This should lay the ground work for other features as well. This example is using Rails 3, but should be easily adaptable for 2. Also, make sure you already have FFMPEG installed and running properly. So lets get started: First things first…we need to add the appropriate gems to our Gemfile:
# Gemfile gem "carrierwave" gem "streamio-ffmpeg"
Next is the meat and potatoes of this..the actual FFMPEG process for carrierwave. I choose to keep my plugin files in the directory lib/carrierwave. Make sure you have this path included in your application.rb file if you are using rails 3. Here is the code:
# lib/carrierwave/ffmpeg.rb require "streamio-ffmpeg" module CarrierWave module FFMPEG module ClassMethods def resample( bitrate ) process :resample => bitrate end end def resample( bitrate ) directory = File.dirname( current_path ) tmpfile = File.join( directory, "tmpfile" ) File.move( current_path, tmpfile ) file = ::FFMPEG::Movie.new(tmpfile) file.transcode( current_path, :audio_bitrate => bitrate) File.delete( tmpfile ) end end end
Good. Now that we have the plugin coded up, we need to include it into our uploader. I already have one mounted to my Asset model. Here is what my AssetUploader now looks like:
# app/uploaders/asset_uploader.rb require File.join(Rails.root, "lib", "carrier_wave", "ffmpeg") class AssetUploader < CarrierWave::Uploader::Base include CarrierWave::FFMPEG # <= include the plugin # Choose what kind of storage to use for this uploader: storage :file # Override the directory where uploaded files will be stored. # This is a sensible default for uploaders that are meant to be mounted: def store_dir "#{Rails.root}/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end # Add a version, utilizing our processor version :bitrate_128k do process :resample => "128k" end end
There! Now whenever you add a new file, it should fire off the processor and create a new version. I hope this help anyone still up in the air about how to put together their own plugin/process for carrierwave. Next I will demonstrate how to incorporate Delayed::Job to move these intensive tasks to the background!
Cool, I’ll try it out later, a multimedia app can really use this 🙂 10x
I’m fairly new to Rails and why I try to submit the form with an .mp4 file I get:
undefined method `resample’ for #<#<Class:0x0000…
I also don't know what to add into my application.rb file.
Any help?
Cheers,
Scott
Ignore my last comment. I had missed requiring a file.
I did however have to change the File.move call to FileUtils.mv instead as Ruby complained that there was no Move method on File.
Thanks for the guide!
Got this mostly working – Thanks for the guide! I am converting videos to .mp4 format. The upload completes fine and stores in the default: “uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}”. The temp file is created in a directory under uploads/tmp, and the transcode processes correctly. But the output .mp4 file remains in the tmp directory so when I reference the model’s _url property it references the uploaded file, not the transcoded file. Any idea how I could modify this to have the processing happen before the model’s save?
I am new in ror and so in javascript.
I’m using paperclip to attach images and videos and jw-playr to display the video.
My Client changed the requirement and asked to show an image till user click on PLAY button and when it click on STOP the image show be visible again.
I have no idea how to do it. I have gone through swfobject.js and jw_player_helper.rb but couldn’t understand how to do?
I also thought to use onClick over the image il show on the player but problem is how to control the PLAY and STOP button.
Any idea? I would really appreciate any help.
Thanks in advance.
Sham
I’m new and I can’t figure this out. I have a thumbnail model and a video model. I want to upload a video and have a carrierwave ffmpeg process create a thumbnail of the video. Is it even possible to create a thumbnail object through a video objects uploader process? Is there a better way to do what I am trying to do?
Jim – You wouldn’t want to do it before the save and that would involve the user waiting for the processing to run, unless you utilized some type of temporary table. The file should be created from the temporary file, but written to the current path. Maybe you overlooked something?
Sham – You would want to update the processing of the video to take a snap shot and save that image. Its definitely possible. JW Player should have the ability to display an image until the video starts playing by setting up the config properly.
Carlos – Yes its possible, as I told Sham. You would just change the transcoding in this example to snap a picture of a frame in the video. Check the docs for ffmpeg.
As Carlos I am trying to get a thumbnail version of the video…
got it running w ffmpeg BUT I don’t see how to output the file correctly so CarrierWave will store it next to the video clip..
I wrote in video_uploader
version :thumbnail do
process :videothumb
end
and in lib/ffmpeg.rb
def videothumb
process :videothumb
end
end
def videothumb
directory = File.dirname( current_path )
tmpfile = File.join( directory, “tmpfile” )
FileUtils.mv current_path, tmpfile
file = ::FFMPEG::Movie.new(tmpfile)
file.transcode(current_path, “-f image2 -vframes 1 ‘output_thumbnail.jpg'”)
FileUtils.rm tmpfile, :force => true
end
thus works well… but , the output_thumbnail.jpg is created at Rails app root level…
as the ffmpeg command requires an output file name, how can I get the image created passed back to CarrierWave to create the version :thumbnail itself ???
writing :
file.transcode(current_path, “-itsoffset -4 -vcodec mjpeg -vframes 1 -an -f rawvideo output.jpg”)
thumbnail version is created in the /tmp/uploads directory , close to the video :
/tmp/uploads/20110516-1823-3814-0750/davis.mp4
/tmp/uploads/20110516-1823-3814-0750/thumbnail_davis.mp4
it should be written with a .jpg extension
Randy – as far as i understand it, this only works if the filename is not changed. i tried to convert a video into flv format but the converted video file is not copied to the store_dir and left in tmp dir. any idea how to accomplish this?
Off hand, I am not sure how to approach these issues quickly. I will try to work on an example that will do both creating a thumbnail and also converting a video when I get some spare time.
that sounds very nice randy, thank you. I will try to find a solution for the converting issue also.
Hello,
Thanks for your tutorial. I managed to get it working on Rails 3.1 and the versions are stored correctly but they dont get converted by ffmpeg they remain untouched.
any idea what could be the problem?
thanks!
Did you ever get round to working on an example that will both create a thumbnail and convert a video to a different format? – I’m running into the same issues as most people here 🙁
Hey, sorry I have not. My wife and I recently had a new baby, so I have kind of been pre occupied with the family.
I will try to help out when I can.
Hi.
This does not work for me:
File.move( current_path, tmpfile )
I am getting this error:
undefined method `move’ for File:Class
O.K. move is an instance method. Don’t want to make a File.open first.
It works for me with if I use :
FileUtils.mv( current_path, tmpfile )
I get this error when trying to create a new video, or when trying to go to any of the video pages:
uninitialized constant CarrierWave::FFMPEG
Does it have anything to do with the path in application.rb which I didn’t add? (how do you do that btw?)
Hope someone answers.
thanks!
hi, does any one find the solution the problem.
When converting thumbnail it do not change the extension to png if change it explicity it do not copy that file to store and kept it in tmp folder.
Any help?