Transcoding for the Internet Tablets the smart way

A while ago I wrote the “official” video transcoding for Maemo how-to based on the wiki page and some heuristics.

The interesting thing here is the intelligent script that automatically finds the right parameters to use for the encoding.

Some of the considerations include:

  • As higher framerate as possible. For smooth videos.
  • Keep aspect-ratio. So the videos don’t look weird.
  • No cropping. You might miss something.
  • Single pass. Bigger files, but constant CPU usage.

Is this thing really smart? Let’s try it.

Crappy clip from YouTube

transcode.rb -v -i youtube.flv
* Input: 320x240 [4:3], 29.917 fps, 0 kbps.
* Output: 320x240 [4:3], 29.917 fps, 400 kbps.
mencoder youtube.flv -o youtube_it.avi -srate 44100 -oac mp3lame -lameopts vbr=0:br=128 -af volnorm -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=400 -ofps 29.917000

Nothing special, the script maintained all the parameters, so there shouldn’t be any problems. Also, the aspect-ratio is not so different from the one of the device. This one is OK.


transcode.rb -v -i dvd://1
* Input: 720x480 [3:2], 29.97 fps, 7500000 kbps.
* Output: 400x240 [5:3], 29.97 fps, 400 kbps.
mencoder dvd://1 -o 1_it.avi -srate 44100 -oac mp3lame -lameopts vbr=0:br=128 -af volnorm -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=400 -ofps 29.970000 -vf-add scale=400:240

In this case the aspect-ratio is slightly modified (less than 10%), but the target aspect-ratio is exactly the same as the one of the device, so, it will look good.

So far the script does seem intelligent 🙂

High Definition

Now let’s try a non-standard resolution, high bitrate, and H.264 format.

transcode.rb -v -i starcraft.divx -f h264 -q 8
* Input: 1024x436 [256:109], 23.99 fps, 1917920 kbps.
* Output: 352x144 [22:9], 23.99 fps, 1500 kbps.
mencoder starcraft.divx -o starcraft_it.avi -srate 44100 -oac mp3lame -lameopts vbr=0:br=128 -af volnorm -ovc x264 -x264encopts bitrate=1500:nocabac -ofps 23.990000 -vf-add scale=352:144

The aspect ratio now is about 4% different, that’s good. Unfortunately the aspect-ratio is quite different from the one of the device, but that’s not too bad.

This is how it would look like:


note H.264 videos look pretty good on the device.


So far there hasn’t been a single video where I had to modify the parameters that this algorithm suggested; sometimes the quality is not that good but increasing it with the -q option does the trick.

Wondering if it would find the right parameter for your clips? Why don’t you try it and find out 🙂

The script is here. And the garage project here.


GStreamer hello world

Continuing my previous GStreamer introduction this new tutorial will guide you on your first GStreamer application written in C.

For anxious people the code is here.

The Basics

We first start creating a playbin, which is a GStreamer element:

GstElement *pipeline = gst_element_factory_make("playbin", "player");

Then we specify the URI we want to play, i.e. file://tmp/foobar.mp3

g_object_set(G_OBJECT(pipeline), "uri", uri, NULL);

Now, we would want to play this:

gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);

But nothing would happen if we don’t have a GLib mainloop:

GMainLoop *loop = g_main_loop_new(NULL, FALSE);

And of course, we need to initialize GStreamer:

gst_init(&argc, &argv);

or, if you don’t have argc/argv available:

gst_init(NULL, NULL);

That’s it, at the end you should stop the pipeline and free it:

gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);


Nothing complicated here, pkg-config will tell us all we need to know:

gcc `pkg-config --cflags --libs gstreamer-0.10` hello.c -o gst_hello

The Bus

Great! we have something that plays, but it won’t even exit properly, nor show any errors.

In order to get messages, errors, and other important information while the mainloop is running we need a bus watch from this pipeline:

GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
gst_bus_add_watch(bus, bus_call, NULL);
static gboolean
bus_call(GstBus *bus,
	 GstMessage *msg,
	 gpointer user_data)
	switch (GST_MESSAGE_TYPE(msg)) {
	case GST_MESSAGE_EOS: break;
	case GST_MESSAGE_ERROR: break;
	default: break;

	return true;

When we get the EOS message, we would like to exit the application, right?


And if we get an ERROR message, we would like to display it, and exit:

gchar *debug;
GError *err;

gst_message_parse_error(msg, &err, &debug);

g_error("%s", err->message);


It seems we have everything!


Now you can listen to your music or watch videos with this. And remember, it receives a URI, so “test.avi” won’t work, you need the whole thing:

./gst_hello "file://$PWD/test.avi"

Or even better!