Using MMS2R for mobile integration with Rails

Posted by Luke Francl
on Thursday, May 24

At Slantwise, we’ve been doing some projects that involve taking user-submitted content from cell phones. Using an MMS-to-email gateway, it’s straightforward to ingest photos and videos into a Rails application with ActionMailer. Every phone we’ve tested has the ability to send an MMS message to an email address, so this is a cheap and easy way to get started. We’d like to use a shortcode like Twitter’s 40404 some day, but they are hellaciously expensive.

While receiving the email is straightforward, you still have to deal with the advertising and general crap that is added to the messages by the phone carriers.

Here’s an example from Sprint, by far the worst carrier we’ve come across:

Sprint adds advertising and other garbage to your MMS message

Not only is this message stacked with ads and other nonsense, the worst part is that the photo isn’t actually included as an attachment! You have to download it from Sprint’s server.

Thankfully, we found the MMS2R library, created by Mike Mondragon. MMS2R greatly simplifies processing MMS messages. MMS2R removes advertising, eliminates default subjects, and makes fetching media from the message much easier. It even has a special case to download the real media for Sprint messages. MMS2R decodes and extracts files from multipart MIME email so you don’t have to!

Imagine you have a MediaItem model that has a title and a file associated with it (we used AttachmentFu to store the files). Here’s an ActionMailer you that will process an incoming MMS message and store it as a new MediaItem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
require 'mms2r'
require 'mms2r/media'

class IncomingMmsHandler < ActionMailer::Base

  def receive(email)
    # create a new Media Item
    item = MediaItem.new
    
    begin
      # Parse the MMS attachments with MMS2R
      mms = MMS2R::Media.create(email)
      mms.process

      # Gets the subject, stripping out known carrier defaults
      item.title = mms.get_subject
    
      # Get the most likely media for the message
      # MMS2R mocks up a CGI.rb temp file object, 
      # so get_media can be used with AttachmentFu!
      item.uploaded_data = mms.get_media
    
      # persist the item
      item.save!
    ensure
      # clean up the temp files      
      mms.purge
    end
  end
end

The error handling in this example is rudimentary for simplicity’s sake.

This all works on a per-carrier basis. The currently supported carriers are:

  • AT&T/Cingular
  • Dobson/Cellular One
  • Nextel
  • Sprint
  • T-Mobile
  • Verizon

New rules have to be added to strip advertisements from unknown carriers (you’ll still be able to access the media). New carriers are easy to add, so don’t be shy about submitting a patch.

MMS2R is being used in production at several web sites including mymojobaby, Vedio.tv and our own project, which is still in private beta.

(This is a preview of some of what I’ll be speaking about at Ostrava on Rails. The speakers have just been posted and it looks like a great line up. Don’t miss it if you’re in Europe.)

Rose^h^h^huby Colored Glasses

Posted by Dan Grigsby
on Saturday, May 19

Earlier this year, I attended Avi Bryant’s Applied Web Heresies tutorial session at ETech. Starting from scratch, over about four hours, the other students and I each created a mini-Seaside framework in the language of their choice.

The details of the tuturial are worthy of a post, and Nick Sieger and I are working on one. For this post, I’ll just use his thesis from the talk as a starting point. Avi says,

“A lot of the design decisions that are unconsciously adopted by modern frameworks like Rails or Django were made 10 or 15 years ago. They were probably good decisions at the time, but we need to re-evaluate them now that we have new and better tools available to us.”

The “unconsciously adopted” part of his thesis has been bouncing around in my head since then, and showed up in an unexpected place that I want to explore – think out, really – in this post.

To get where I want to go with this post I’ll need to go a bit off-topic for a couple of paragraphs, but I’ll bring it back quickly, so bear with me.

About a month ago, a group of us – including fellow Rail Spikes contributors Luke and Jon – decided to teach ourselves functional programming. With the help of the Pragmatic Programmers’ Beta Book, I decided to tackle Erlang.

Erlang’s primary data structure unit is a “tuple.” Adapting an example from the book, here’s a tuple that represents a single contact in an address book:

1
2
3
4
5

{person,
  {first_name, "Dan"},
  {last_name, "Grigsby"}
}.

Even if you’ve never seen erlang before, it should be pretty clear what’s going on. And, of course, an address book would normally have more fields, but this simplified example will be good enough.

Like Ruby, Erlang uses square brackets to signify lists. A list of people in an address book would look like this:

1
2
3
4
5
6

AddressBook = [
  {person, {first_name, "Dan"}, {last_name, "Grigsby"}},
  {person, {first_name, "Kristy"}, {last_name, "Grigsby"}},
  {person, {first_name, "Dan"}, {last_name, "Buettner"}}
].

Now that we’ve covered this little bit of Erlang, it’s time to bring this post back onto topic. Recalling Avi’s unconscious adoption thesis, I want to talk about about databases and ORMs.

As Rails developers, we spend a lot of time working to “round trip” Ruby objects into and out of the database. While ActiveRecord makes this translation considerably simpler than other ORMs, ultimately we still put in work to get what we want:

We want Ruby objects.

As an experiment, let’s think about working exclusively with lists of pure Ruby objects within the context of the CRUD (Create, Read, Update, Destroy) verbs, since that reflects pretty well what we do with ActiveRecord.

To keep this discussion anchored, let’s port our Erlang Person tuple/record to Ruby and work with that:

1
2
3
4
5
6
7
8

class Person
  attr_accessor :first_name, :last_name
  
  def initialize(first_name = nil, last_name = nil)
    @first_name, @last_name = first_name, last_name
  end
end

We want an Address book, which we’ll represent as a list of Person objects, so let’s add that:

1
2
3
4
5

address_book = []
address_book << Person.new('Dan', 'Grigsby')
address_book << Person.new('Kristy', 'Grigsby')
address_book << Person.new('Dan', 'Buettner')

Before we go on, we should take a second to think about storage:

Obviously, Ruby objects can be stored in memory; keep them in a long running process and you’d be able to work with them through a series of http requests. Serialize them and you can store them on disk for when your long-running process dies or needs to be shut down.

Alright, back to CRUD verbs:

Let’s knock down the basics quickly:

Create is demonstrated above: use the Person.new method to create an object and then add it to the address_book list. To Read an object, reference it by its index: address_book[0]. Update follows suit: address_book[0].first_name = 'Bob'. To Destroy, use any of the delete/slice/shift/pop array methods.

You probably noticed that I cheated a bit. My Read example is the equivalent of an ActiveRecord find using an ID. If you happen to know the list-offset then you can retrieve your element. The question becomes: how can we selectively choose element like you could do in a WHERE part of a SQL clause or ActiveRecord :conditions argument?

To answer this quetion, I’ll select out a list of people whose first name is Dan. In SQL, this’d be SELECT * FROM PEOPLE WHERE FIRST_NAME = 'Dan'. In ActiveRecord, :conditions => ["asks.state = 'open'"] would do it. With an array of Person objects, in Ruby this would do it:

1
2
3
4
5

dans = address_book.inject([]) do |results, person|
  results << person if person.first_name == 'Dan'
  results
end

(Update: See Chris Carter’s comment below about using Enumerable’s .select for a better way to do this.)

Similarly, Ruby’s Array method “delete_if” could be used to selectively delete from the list.

Using nothing other than pure Ruby objects and standard, simple Array methods we’ve demonstrated most of the common uses of ActiveRecord. We never had to leave Ruby to do this.

There’s a simplicity and an odd-elegance to this that appeals to me.

If that sounds crazy to you, consider that Mnesia, Erlang’s wickedly fast, fault-tolerant DBMS operates in the mode I’ve described the process here: it stores lists of native Erlang records (in memory, to disk, or both) and you apply list functions to select items you want. Here’s the select statement demonstrated above in SQL, ActiveRecord and Ruby, but this time in (I’m sure very bad) Erlang:

1
2

[ {person, {first_name, "Dan"}, {last_name, X}} || {person, {first_name, "Dan"}, {last_name, X}} <- AddressBook ].

Erlang has Mnesia. Someone who wears “rose colored glasses” could be said to have selective amnesia. Maybe the world needs an adaptation of Mnesia to Ruby: Ruby Colored Glasses.

I’m certain that there are more or less complete Ruby variants of Mnesia. The purpose of this article isn’t to suggest that there should be another one – if you’ve used one, use the comments to tell me about it. Rather, this post examines we can learn by throwing out one of the things we’ve all “unconsciously adopted” with Rails. Stay tuned for my forthcoming – and aforementioned – article on the mini-Seaside I built in Avi’s tutorial where we’ll question other assumptions to interesting ends.

BingD'OH!

Posted by Luke Francl
on Friday, May 18

As I hinted on Tuesday, we’ve developed a fun little app for RailsConf: BingD’OH!

BingD’OH! makes meetings tolerable by letting you create your own buzzword bingo games. This codebase is evolved from one of the first Rails apps I ever wrote. Jesse Ross and I hacked out the first version during Martin Fowler’s keynote at RailsConf 2006.

BingD'OH card

For this year’s RailsConf, the Slantwise crew and I cleaned it up and made it possible to create your own games. Norm Orstad gave it a nice retro “church basement bingo night” design. Now it’s the best-looking buzzword bingo site on the web. Check it out.

RailsConf slides (video transcoding)

Posted by Jon
on Friday, May 18

Here are the slides from my RailsConf presentation today on building a video transcoder workflow. I’ve taken out the video; if you’re really interested in seeing it, send me an email. I’ve also created a version without any images at all in order to save space.

Video transcoding slides, with images (4.5M)

Video transcoding slides, no images (207K)

I’d also love your feedback from the talk if you were there.

Video Transcoding, part 3: Asynchronous Processing (overview)

Posted by Jon
on Thursday, May 17

(This is part 3 in a series on video transcoding. Parts 1 and 2 covered video formats and codecs, and transcoding tools, respectively.)

Ruby on Rails is a great technology that gives you a lot for free. You get ORM, MVC, templating, HTML helpers, URL rewriting, database schema management, a development web server, prototype, script.aculo.us, deployment automation, a unit test framework, etc. etc. etc. But what you get falls into two basic categories: tools to handle a HTTP request, and tools to make the application easier to manage. In other words, Rails helps you out when something is either triggered directly by an HTTP request, or when it is triggered directly by a developer. This is because the Rails environment fires up and tears down with every HTTP request.

Unfortunately, a video transcoding system can’t be run in either of these ways.

First, video transcoding can’t be done within the space of an HTTP request. Transcoding jobs can take several minutes (or hours), and you can’t expect your users to wait that long. If a request takes longer than a few seconds, a significant number of users will cancel the request. Beyond this, since Rails isn’t thread-safe, every video transcoding job would cause a Mongrel instance to block.

Second, video transcoding could theoretically be triggered manually by an operator, but this solution isn’t responsive or scalable. What happens when your application takes off, and you have dozens or hundreds of files per day?

In my next few posts, I will examine three approaches to asynchronous processing, and will discuss three ways to host your asynchronous system. The use case will be video transcoding, but the approaches themselves could be used for just about any time-consuming action: expensive calculations, large PDF creation, data processing, etc.

1. Database polling with a daemon – This is the simplest approach, and it isn’t a bad one. All you need is one or more transcoding servers with access to your main Rails application database, and maybe some shared disk space. The transcoding servers query the database for unassigned jobs, and the first server to select the job takes it. Then, when the work is done, the transcoding servers update the database with the new state.

2. Message queue polling – Instead of polling a database, this solution uses a message queue (like Amazon SQS). The base application passes a message to the message queue announcing the new job, and the transcoders poll the queue looking for jobs. This approach is a little more complex than the first approach, but is a little more secure, robust, and scalable.

3. Reactor-pattern system – This solution doesn’t involve polling at all. Instead, a controller accepts jobs and assigns them directly to individual workers. This approach is the most complex of the three, but will satisfy the purist’s desire to avoid polling.

I’ll discuss each of these approaches in their own articles in detail. There certainly are other approaches; if you have a favorite that you don’t see here, post a comment.

Finally, I’ll look at three ways to host these approaches: local to your main application, in its own dedicated hosting environment, and using Amazon Web Services.

Stay tuned for the next post. I’m at RailsConf right now, but I’ll try to get something posted within the next few days.

Using the UrlKey plugin for pretty, unique URLs

Posted by Luke Francl
on Tuesday, May 15

I really like “pretty” or human-friendly URLs. One way to get them with Rails is to use Rick Olson’s PermalinkFu plugin. PermalinkFu takes care of making a field in your model URL-safe. It escapes non-ASCII characters, removes illegal characters (like ? and /), turns spaces into dashes, and then takes the whole string to lower case.

PermalinkFu is great, but we found it didn’t meet our needs for Better Together.

There are two problems with PermalinkFu. First, it re-creates the permalink every time you save the record, making it not very permanent. Second, the permalinks are not globally unique, so they need to be scoped in some way. This isn’t a problem with blogging engines (with a URL scheme like /2007/05/15/slug how much uniqueness do you need?), but we needed something that would create globally unique permalinks which we could use as lookup keys.

So we wrote the UrlKey plugin to address those needs.

UrlKey takes the same approach to URL-ifying a string as PermalinkFu, but it also ensures that the key is globally unique by appending a number before saving it. It also only sets the key when you create a new record, so if the name changes, the key will not.

Using UrlKey

First, install the plugin:

script/plugin install svn://rubyforge.org/var/svn/slantwise/url_key/trunk

Then create a migration for the model you want to give a unique, human-friendly identifier. By default, UrlKey will look for a column called url_key, but you can call it whatever you want.

1
2
3
4
5
6
7
8
9
10
class AddUrlKey < ActiveRecord::Migration
  def self.up
    add_column :games, :url_key, :string
    add_index :games, :url_key
  end

  def self.down
    remove_column :games, :url_key
  end
end

(This code is from a fun little app we’re working on. Keep your eyes peeled: it launches at RailsConf.)

You’re going to want an index on the key column because it will be used in lookup queries.

To your model, add the has_url_key class method and create a to_param method:

1
2
3
4
5
6
7
class Game < ActiveRecord::Base
  has_url_key :name
  
  def to_param
    url_key
  end
end

The to_param is used by Rails in URL generation. Usually it returns the ID of the object. By changing it to return the url_key, we’ll generate friendly URLs automatically.

But since we’re overriding to_param, we need to make a change to the controller to look up the Game by its url_key:

1
2
3
def show
  @game = Game.find_by_url_key(params[:id]) || Game.find(params[:id]) 
end

Now the GamesController will first look for the Game instance by key, and then by ID. That way any old URLs will still work, and you can use IDs if you want to.

Caveats

UrlKey may not be for everyone.

The uniqueness check will run a query for each iteration until it finds a unique name, so if you have a lot of data this will suck.

Right now, there’s intentionally no facility for re-naming a URL key. It might be a nice feature if combined with a 302 redirect from the old URL.

Other approaches

There are several other plugins that accomplish similar tasks.

PermalinkFu (noted above) is probably the most widely-used.

Others automate the common technique of using the ID in the URL, followed by a slug (for example: /articles/849-my-title-here). Obie Fernandez explains this approach. Two plugins to accomplish this are ActsAsSluggable and Friendly Param. I think these URLs look ugly, but they are easy to create with Rails.

Slider.js - a javascript slider component

Posted by Bruno
on Monday, May 14

Update: this component has been renamed to Glider.js

Inspired by the cool sliding window thingy at Panic, I decided to whip something up that I could use on Curbly.

Glider.js depends on Prototype and effects.js (from the Scriptaculous library), and it’s available for download here.

Here’s what it looks like:

7 Writing Tips for Programmers

Posted by Bruno
on Friday, May 11

What’s harder: code or prose? For me, it’s easier to write Ruby than English, because if your syntax is wrong in Ruby, the code just won’t run. In prose, improper syntax, spelling mistakes, and made-up words can slip into published writing.

Broken prose still runs, but it doesn’t reflect well on you as a programmer or a person.

Here are some tips that will help make sure your next blog post doesn’t read like code spaghetti.

1. That’s Excessive

It’s easy to overuse the word “that”. Cut that out! Your writing will remain readable and contain a lot less cruft.

2. Write Like You Code

You want your code to be elegant, simple, and clean. Keep your prose that way.

  • Re-factor paragraphs for length (shorter please).
  • Bad syntax means buggy code. Bad grammar makes for bad reading.
  • Write first, optimize later.

3. “Say It Ain’t So,” he said.

Attribution is tricky. Do it wrong, and it’ll slow the reader’s progress.

  • Commas and periods always go inside the quotation marks: “At last,” said the old woman, “I can say I am truly happy.”
  • Questions marks and exclamation points go inside when they are part of the quote: My mother asked, “How cold is it outside?”
  • Don’t quote when you can paraphrase: She called the man “a nice person” is pointless, but She called the man “a giant booby” should probably be quoted.

4. Possessives, Plurals, and URLs

  • Joseph didn’t wear a coat of many color’s (it’s colors).
  • The internet doesn’t need more URL’s (it’s URLs).
  • Burt Reynolds’ possessives skills are sharp; much sharper than Joaquin Phoenix’s (not Joaquin Phoenix’).

5. Teh interweb Makes Up Words …

... but that doesn’t mean you have to use them. You’re not wearing a tshirt while sending email. Both words deserve dashes (T-shirt and e-mail). Cyberspeak is appropriate, because cyber has become an accepted prefix, like bio in biotech. Haxors are l33t, unless you are a noob, because then you won’t know what the hell they’re talking about.

6. Don’t Pad Your Sentences

When you don’t know what you’re talking about, stalling with junk words is not a remedy:

Take into consideration the fact that in order to be a good programmer you must also be a good writer.

This is bad. It can be shortened:

Consider that to be a good programmer you must also write well.

7. Know When to Stop

This is one of the hardest parts of writing. I’ve had nearly-completed posts languish in the draft pile for weeks, lacking a proper ending. That’s a sign you’ve written too much. Go back, find the purpose of your piece, and say it again at the end. More often than not, you’ll be surprised how good an ending it makes.

Bottom line: you are what you write. That applies to your blog posts, your code, and even your e-mails and instant messages. How you write tells others a lot about how you think. So take a little extra time to make sure your words are well-chosen.

Capistrano 1.4.1, net-ssh 1.1.0 issue - Net::SSH::HostKeyMismatch

Posted by Casey
on Thursday, May 10

I recently ran into an issue after upgrading my gems in which capistrano would fail during the SSH known_hosts verification. I’m on OS X with capistrano 1.4.1, net-ssh 1.1.0 and can manually SSH to the deploy machine. A little googling turned up an easy work around for the problem. Simply add the following line to your deploy.rb and capistrano will skip the known_hosts verification.


  ssh_options[:paranoid] = false 


Uploads with respond_to

Posted by Luke Francl
on Tuesday, May 08

File this one under “Stupid Rails Tricks.”

You’re probably familiar with using respond_to in your controller to generate different content representations based on the HTTP Accept: header or the URL extension:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def list
  respond_to do |wants|
    wants.html {
      @people = Person.find(:all)
    }
    wants.csv {
      people = Person.find(:all)
      csv = FasterCSV.generate do |csv|
        csv << ["user name", "first name", "last name", "email"]
        people.each do |person|
          csv << [person.user_name, person.first_name, person.last_name, person.email]
        end
      end

      send_data(csv, :filename => 'people.csv', 
                :type => 'text/csv', :disposition => 'attachment')
    }
  end
end

With RESTful routing, the first form gets the named route people_path, and the second form gets formatted_people_path(:csv).

But did you know you can use respond_to on uploads, too?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def create
  respond_to do |wants|
    
    wants.html {
      @person = Person.new(params[:player])
      @person.group = @group        
      if @person.save
        redirect_to people_path(@group)
      else
        flash[:error] = "Person wasn't saved."
        render :template => 'person/new'
      end
    }
    
    wants.csv {
      row_count = import_csv(params[:csv_file]
      flash[:message] = "Imported #{row_count} players."
      redirect_to people_path
    }
  end
end

If you POST a multipart form to formatted_people_path(:csv) you’ll get the CSV creation method.

This can be used to keep your URLs clean and you controllers a little smaller. I’m not sure if it’s very useful, but it is kind of neat.

Video transcoding, part 2: tools

Posted by Jon
on Monday, May 07

(This is Part 2 in a multi-part series on video transcoding and the web. For the rest of the series, take a look at the first post.)

There are a lot of tools that can be used to transcode video and audio. These fall into four main categories.

First are desktop applications that can “Export” to another format. iMovie, Final Cut, Premiere, Windows Movie Maker, Flash, and a host of other applications fall into this category. These tools are great for personal, low-volume use, but they require a person to manually load up each job, so they aren’t suitable for integration with another application.

Second are enterprisey apps that primarily do video transcoding, with a GUI frontend to make things easy. These applications watch an input folder, apply a recipe (like “AVI/MPEG-4, 2000kbps”), and send the file to an output folder. Telestream FlipFactory is an example. I haven’t actually used FlipFactory, though I’ve talked with people who say that it offers everything you’ve come to expect in enterprise software. (Ahem.)

Apple Compressor is a lightweight version of this, with a low price, poor documentation, a non-existent user community, slow transcoding speeds, and good quality output. Compressor has some nice built-in workflow and distribution tools. From what I understand, Compressor is kind of like a GUI on the Quicktime transcoding libraries.

Third are asset management tools that do video transcoding as one component among many. These are kind of like FlipFactory (and several actually make use of FlipFactory), but they have a lot of other things tacked on as well. If you need an asset management tool, this might be a good option. But if you want a website that transcodes and publishes user-submitted videos, these tools are like buying a PC for its calculator app.

Fourth and finally are command-line tools that do video/audio encoding, and nothing else. Examples include ffmpeg, mencoder, and On2 Flix Engine. These tools take an input file and a lot of other options and create an output file. Most are open-source. Most (all?) run on Unix/Linux.

It is probably clear that I’m most interested in the fourth category of tools (though Apple Compressor is at least moderately interesting). The first category isn’t suitable for building a robust system, and the second and third categories are expensive and heavyweight. ffmpeg and mencoder are free, and On2 is relatively cheap, and all three do a good job. So let’s explore them in a bit more detail.

Overview of tools

ffmpeg and mencoder

Notice that I’ve lumped these together. That is because ffmpeg and mencoder are kind of like C++ and Objective-C; they do the same thing, in similar ways, with the same foundation, though they aren’t identical. Both ffmpeg and mencoder use the libavcodec and libavformat libraries for the bulk of their codecs and formats. (libavcodec and libavformat are part of the ffmpeg project, and are LGPL’d.)

I’m not an expert on these tools, so I haven’t used ffmpeg and mencoder enough to be able to recommend one over the other. They have their differences; mencoder supports more powerful filters and advanced options. But I’ve used both successfully, and they are ultimately quite similar. (If you have extensive experience with both of these, and want to weigh in on the matter, leave a comment below.)

On the whole, these are two of the most impressive open source projects I’ve ever seen. They are powerful and reliable, with good speed, good quality, and a huge range of supported formats.

ffmpeg and mencoder have one downside: support. The easy things are easy, but the hard things are painful, and you’re basically on your own when it comes to figuring things out. Don’t look for a good manual, or a corporate support contract, or even a few local experts you can hire. The experts are young hackers from around the globe who hang out at http://forum.doom9.org or on the various mailing lists. Also, as good as they are with the complexities of ffmpeg and mencoder, their primary use case is ripping archiving DVDs, not building robust video transcoding systems.

This doesn’t just mean that you won’t squeeze every last drop of power out of ffmpeg. It also means that you will run into strange errors, and your only source of help will be mailing lists and deep Google searching.

Here are a few code examples that illustrate the range of complexity behind these tools. The following command transcodes an input file to MPEG-4 AVC:


ffmpeg -i matrix.mov -vcodec h264 -ab 128 -s 720x304 -r 23.98 matrix-h264.mp4

The following command also transcodes an input file to MPEG-4 AVC, but does a better job:


ffmpeg -y -i matrix.mov -v 1 -threads 1 -vcodec h264 -b 500 -bt 175 -refs 2 -loop 1 -deblockalpha 0 -deblockbeta 0 -parti4x4 1 -partp8x8 1 -partb8x8 1 -me full -subq 6 -brdo 1 -me_range 21 -chroma 1 -slice 2 -max_b_frames 0 -level 13 -g 300 -keyint_min 30 -sc_threshold 40 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.7 -qmax 35 -max_qdiff 4 -i_quant_factor 0.71428572 -b_quant_factor 0.76923078 -rc_max_rate 768 -rc_buffer_size 244 -cmp 1 -s 720x304 -acodec aac -ab 64 -ar 44100 -ac 1 -f mp4 -pass 1 matrix-h264.mp4

ffmpeg -y -i matrix.mov -v 1 -threads 1 -vcodec h264 -b 500 -bt 175 -refs 2 -loop 1 -deblockalpha 0 -deblockbeta 0 -parti4x4 1 -partp8x8 1 -partb8x8 1 -me full -subq 6 -brdo 1 -me_range 21 -chroma 1 -slice 2 -max_b_frames 0 -level 13 -g 300 -keyint_min 30 -sc_threshold 40 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.7 -qmax 35 -max_qdiff 4 -i_quant_factor 0.71428572 -b_quant_factor 0.76923078 -rc_max_rate 768 -rc_buffer_size 244 -cmp 1 -s 720x304 -acodec aac -ab 64 -ar 44100 -ac 1 -f mp4 -pass 2 matrix-h264.mp4

Note that the command occurs twice, almost identically; this is a two-pass job, so the same command is executed twice. Two-pass encoding can create more efficient files, since the second pass learns from the first pass.

Supporting tools

ffmpeg and mencoder are great, but they don’t stand on their own – just like Linux is great, but it doesn’t do much without GNU tools. ffmpeg and mencoder handle dozens of codecs and libraries, but several common (and important) libraries are handled elsewhere. Want H.264? Use x264. What about MP3? LAME mp3. AAC? faac and faad. These programs can run on their own, or support can be compiled directly into mencoder and ffmpeg. The latter option is usually preferable to keep things simple, but of course, this doesn’t always work. You may need to transcode in multiple steps, because ffmpeg doesn’t support your desired codec, or because you can get better quality by doing things separately. For this reason, most of these programs can output to a pipe and take input from a pipe. Otherwise, you can just create temporary files for the intermediate steps.

There are also many tools that do small, specialized tasks, and which are not integrated with mencoder and ffmpeg. Want to export your video as raw YUV frames? Want to repackage a mp4 file as mov? Want to add metadata to a file? Mencoder may be up to the task, but if it isn’t, there are dozens of small tools that can be used to do these things.

On2 Flix Engine

On2 Flix Engine is a commercial video transcoder that outputs VP6 files. This is good: VP6 is comparable to H.264 in terms of quality and efficiency. It works similarly to ffmpeg and mencoder – install it, run a binary with options (input file, output file, quality, resolution, etc.), and that’s it.

I haven’t On2 Flix Engine extensively, but I want to. Why? Becuase it outputs high quality FLV files. This means that On2 offers extremely high quality and extremely high compatibility, unlike flv/h.263 (high compatibility, low quality) or H.264 (high quality, lower compatibility). So it makes a lot of sense for distributing files over the web.

Since I haven’t used On2 much, I asked a friend of mine who has used it extensively, Matt Bauer, for his experience. He posted his thoughts in this article on On2. He’s also working on Ruby bindings for On2, which is very cool.

What about Ruby?

This series is, at least in part, a discussion of how to create a video transcoding system using Ruby. So does Ruby have a place in low-level video transcoding? Short answer: no. Video transcoding is time consuming and processor intensive. Ruby is slow. Bad combination. That doesn’t mean that Ruby isn’t suitable for a high level video transcoding system, as we’ll see in the next post; Ruby is a near-perfect language for gluing together a transcoding system. But for actually decoding a file and reencoding it in another format: stick to C. All of the tools I’ve discussed were written in C, with maybe some rogue C++ or Objective-C here and there. And actually, there is an x264 parallel encoder known as x264-farm that is written in OCaml, which is pretty sweet. But these are all really fast languages, and Ruby is not.

So let’s leave Ruby for our controller code. Stay tuned.

Slantwise goes global

Posted by Luke Francl
on Wednesday, May 02

Two of us from Slantwise (and Rail Spikes) will be going on the speaking circuit this summer.

Jon will be speaking about video transcoding and Rails at Rails Conf 2007. The series of blog posts here at Rail Spikes will form the basis for his talk. Jon will be speaking on the first day of the conference, May 18th.

My talk on mobile integration with Rails was accepted to Ostrava on Rails in the Czech Republic, June 22-23.

I’ll be speaking about how to connect your Rails application to mobile phones with MMS2R and some other techniques. It looks like I’ll have some good company, too!

Both of these presentations have grown out of some recent projects we’ve been working on that we’ll be making public shortly. So stay tuned!