It’s not tough to set up a video-encoding facility in Rails. Here’s a quick run-through with some code examples:
Install BackgroundRb
Video encoding is a long-running process and you don’t want to have to wait for the video encode to finish before the web-request can complete. One strategy to avoid having extremely long web-requests is to have Rails hand off the encoding job to BackgroundRb, a job-scheduling server which does long-running work in a separate thread. The same thing could be accomplished in most other web-languages by spawning a new thread and doing the work, but BackgroundRb gives us a bit of extra love: if multiple users upload a whole bunch of videos at the same time, BackgroundRb will queue them up and work on them in sequence so the CPU doesn’t go through the roof.
So, install it and its dependencies.
The BackgroundRb server gets started as a standalone daemon process via
script/backgroundrb start
but you won’t need to do that until you’ve got all the other stuff working.
Set up a BackgroundRb worker class
In order for BackgroundRb to encode a video, you’ll need a VideoConversionWorker which knows how to call FFMpeg, the external encoding utility:
# Put your code that runs your task inside the do_work method it will be
# run automatically in a thread. You have access to all of your rails
# models. You also get logger and results method inside of this class
# by default.
class VideoConversionWorker < BackgrounDRb::Worker::RailsBase
def do_work(args)
# This method is called in it's own new thread when you
# call new worker. args is set to :args
logger.info("Entered VideoConversionWorker.")
video_file = args[:absolute_path]
logger.info("File: " + video_file)
id = args[:video_id]
logger.info("Video id: " + id)
the_video = Video.find(id.to_i)
the_video.processing_status = 1 #the_video.PROCESSING #ProcessingStatuses[:processing]
the_video.save
`nice -n +19 ffmpeg -i #{video_file} -ss 00:00:03 -t 00:00:01 -vcodec mjpeg -vframes 1 -an -f rawvideo -s 320x240 #{video_file}.jpg`
`nice -n +19 ffmpeg -i #{video_file} -ab 48 -ar 22050 -s 320x240 #{video_file}.flv`
the_video.processing_status = 2 #SUCCESS
the_video.save
logger.info("done worker code")
end
end
VideoConversionWorker.register
This is the class which defines a “worker” process for Backgroundrb. It’s like a little self-contained daemon sitting there all the time waiting to encode videos, and should be placed in your Rails app in lib/workers (see the BackgroundRb docs for more info if you’re unclear on this).
Tell your create method to hand off the video to the VideoConversionWorker
When a video gets uploaded, the publish controller tells the Video model encode its video. You can see this happen at line 107 here.
The method at line 190 is where the controller tells the video object(s) to encode its video file. I suppose this could be done more cleanly in an after_save call in the model.
The Video model then hands off the video file to the Backgroundrb VideoConversionWorker for encoding (which is triggered via a command-line call to ffmpeg). You should be able to pretty much copy this class as a starting basis for your own Video class.
Whatever you do, don’t forget that call to
include DRbUndumped
as otherwise the video object data won’t be sent over the wire to the external BackgroundRb server due to serialization problems.
Install FFMpeg
You’ll also need an up-to-date version of FFMpeg, which does the actual encoding. Depending on what distro you’re using, this can be a big hassle and you might be best off building from the latest source, as lots of distros package a version of FFMpeg which doesn’t encode sound properly in FLV files (i.e. the Flash video format). You might want to test FFMpeg before blaming Rails by encoding a vid from the command line:
ffmpeg -i thevideofile.mov -ab 48 -ar 22050 -s 320x240 thevideofile.flv
If you need to build FFMpeg from source on a Debian-based system, take a look at this Ubuntu Forums post.
Note: the video conversion worker code above cranks out an encoded flv and a thumbnail jpg.
Install a Flash video player
There’s a video player in the imc-uk-history source which isn’t super-pretty but allows the use of the Flash 7 plugin which is packaged with slightly out-of-date distros like Ubuntu 6.10. Many newer players require the latest Flash 9 plugin. There is a more fully-featured free software project called Flowplayer that also works very well. The basic trick with the Flash video player is that you need a way to tell the player where the JPEG thumbnail and the FLV video file is for the video you want to load. This can get done in the template, like this:
<% if video.processing_status == 2 %>
<div id="videoplayer<%= video.id %>">
<p>This item requires Macromedia Flash Player 7.</p>
<p>To get the latest version please <a href="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" target="_blank">click here</a>.</p>
</div>
<script type="text/javascript">
var so = new SWFObject("/flash/vidplayer.swf?filePath=<%= url_for_file_column(video, 'file') %>.flv&thumbPath=<%= url_for_file_column(video, "file")%>.jpg", "vidplayer", "336", "326", "7", "");
so.addParam("menu", "false");
so.write("videoplayer<%= video.id %>");
</script>
<strong><%= video.title.titleize %></strong><br />
<%= video.body %><br/>
<%= link_to image_tag("icon_video.gif"), url_for_file_column(video, "file") %>
<%= link_to "download original", url_for_file_column(video, "file") %>
<% else %>
<strong><%= video.title.titleize %></strong><br />
<%= video.body %><br/>
<%= link_to image_tag("icon_video.gif"), url_for_file_column(video, "file") %>
<%= link_to "download original", url_for_file_column(video, "file") %>
<p>This video is still being converted, or else there has been some kind of error.</p>
<% end %>
That embeds the video file for an Event (your code might be for an Article or whatever) and checks to see whether the video has finished encoding or not so everything looks alright when the user goes to view their newly-published article
So, basically there are a lot of moving parts to this thing but if you take them in sequence it’s not too tough. If get really enthusiastic I may do a tutorial with a sample Rails app, the code involved isn’t really that huge. I haven’t yet seen anybody do a plugin, maybe that’d be useful?

Ooh, ooh. Definitely plugin-ize this! Need any help?