Archive for April, 2009

Building a Video Transcoding System for Linux

April 28, 2009

IOTK has a video module that provides a wide range of video manipulation and processing capabilities. When it was created, the goal was to support as many video formats as possible while deploying the system on a 64-bit Linux operating system (the specific distribution was Ubuntu 8.0.4). There were a number of challenges in achieving this, described below, but the most difficult one to overcome was finding all of the necessary software for a 32-bit installation on a 64-bit operating system and getting it to work together.

The specific application for which the system was built was designed to take uploaded videos (in almost any format) and transcode them to Flash video for display using a custom Flash video player. The set of files I was operating on went as far back as 2003 and included old 3G2 and Intel Indeo 5.0 video formats.

Let me describe the user interaction with the system. A user would select a video to upload from their computer. Upon completion of the upload, the application would identify the video as a legitimate, recognized video format, instantiate the transcoding system and configure the parameters for it, transcode the video to Flash, and store the resultant file in IOTK’s DataStore repository. A reference to the file would be created in the database to map the DataStore ID to the record created for the user. Upon playback, the Flash video player would contact the server with a “token” identifying which video to retrieve and the video would be progressively downloaded to the player.

To solve this, I implemented a solution using both ffmpeg and mplayer. Why? ffmpeg, at the time, was unable to identify certain video formats (like Intel Indeo 5.0) but mplayer, specifically mencoder, was. The code I designed would first attempt to manipulate the video using ffmpeg since it had the broadest range of capabilities and format support. If certain types of error conditions were returned from ffmpeg the system would attempt the same operation using mencoder. Moreover, this logic needed to be (and is) unit tested against real videos to make sure it worked properly.

Here is the complete documentation on installing a video transcoding system on a 64-bit Linux operation system. I can tell you, I wish I had this documentation when I was performing the original installation! I have successfully installed this on OpenSuSE 10.2 and 10.3, RedHat Enterprise Release 5, and Ubuntu 8.0.4.

A few IOTK implementation specifics:

function vid_file_mime()
A function to return the MIME type of a video file. Why? The Fileinfo module of PHP does not properly return a MIME type for 3G2 mobile video formats. This wrapper function uses various techniques to identify these issues.

function vid_transcode_to_flash_video()
Using the classes below, sets up and executes the routines required for transcoding.

class vid_Params
Defines the parameters to be used when manipulating a video including audio bit rate and sampling rate, video bit rate, number of audio channels, geometry for the resultant video, and what format to use for it.

class _vid_Transcoder
Given a vid_Params object, an input file, and an output file, executes the steps described in this posting to transcode the video.

The unit tests for this system were designed to attempt to transcode real video files to Flash including detecting when the system had to fail back to mencoder because ffmpeg was unable to perform the tasks. I can confirm that this system works properly for the following video formats:

3GP
AVI
MOV
MPG
WMV
Intel Indeo 5.0 AVI
3G2
MOV (with header compression)

You can see this software in action (including a Flash based video recorder) at any of these sites:

Create a video ecard at Jalapenyo.com
Create a video ecard at FidoFlix.com
Create a video ecard at OnfuegO.com
Create a video ecard at BirthdayVideoEcards.com

Table Builder

April 24, 2009

An important aspect of IOTK is its ability to force a standard way of writing code. I have learned that this promotes higher quality software, decreases debugging time, and makes it much easier for developers to pick up the work of others.

There are several ways I am trying to accomplish this, one of which is having committed to PHP as the language to use for all aspects of IOTK. API code, command line interface programs (CLI), even certain types of configuration files are written entirely in PHP. I want things to be consistent because I can move more quickly if I’m constantly working in a familiar, standard environment.

Until recently IOTK really consisted of 3 languages: the PHP API, the SQL used for DDL, and the PL/SQL used for DML. Each of these languages is very different requiring their own standards and knowledge of their ins and outs.

An essential part of IOTK is its Code Generator facility that is able to write a lot of the PHP and PL/SQL that is required to interact with a table. I will spend more time explaining the Code Generator another time. But because of it I found myself spending more and more time editing SQL.

So the goal in creating the Table Builder was to create a PHP interface that allows me to “describe” a table but to separate what I want from the code that produces it. That is precisely what the Table Builder does. I am now able to construct a table using code that looks like this:

co_Table::make('new_table')
    ->int('id', true)
        ->pk()
    ->int('parent_id')
        ->fk('parent_table', true)
    ->varchar('value', 50, true)
    ->date_col('date_created', true, 'sysdate')
    ->iot()
    ->seq()
    ->idx(array('parent_id', 'date_created'), 1);

The indentation is deliberate and I will explain each line:

  • co_Table::make(‘new_table’) Makes a new table object to be used to define a table named “new_table”.
  • ->int(‘id’, true) Defines a column of type integer with the name “id”.  The “true” parameter indicates that the column should be set to not null.
  • ->pk() co_Table keeps track of the active column – the last column that was defined.  Certain co_Table functions can be called that specifically apply to the active column.  pk() is one of those functions.  It tells co_Table to define the primary key, in this case, as the “id” column.  The indentation used for ->int() and ->pk() is used to make it visually clear that the primary key definition belongs to integer column that was just created.
  • ->int(‘parent_id’) Defines a column of type integer with the name “parent_id”.
  • ->fk(‘parent_table’, true) Is similar to pk() except that it defines a foreign key constraint.  “parent_table” is the name of the parent table in which the foreign key can be found and true indicates that the “on delete cascade” clause of the constraint should be enabled.
  • ->varchar(‘value’, 50, true) Defines a column of type varchar2 with the name “value”.  It has a a maximum length of 50 characters and is defined as not null (the true).
  • ->date_col(‘date_created’, true, ‘sysdate’) Defines a column of type date with the name “date_created.”  The true indicates that the column is not nullable and “sysdate” is the default value to be assigned to the column.
  • ->iot() Defines the entire table as an index organized table.
  • ->seq() Signifies that a sequence should be created for this table.  Note that co_Table will handle naming the sequence in the IOTK standard manner.
  • ->idx(array(‘parent_id’, ‘date_created’), 1) Signifies that an index should be created using the columns “parent_id” and “date_created” and compressed by the first column (the 1).

Calling ->get_definition() on the resultant co_Table object will produce the exact SQL required to create the entities I have described above.  You can literally cut and paste the output in SQL*Plus and it is always outputted in the IOTK standard way for DDL.

Here is the code I would normally type to create the entities described above and the output that co_Table would produce:

create table new_table(
    id integer not null
        constraint new_table_pk primary key,
    parent_id integer default null
        constraint new_table_parent_fk foreign key
            references parent_table on delete cascade,
    value varchar2(50) not null,
    date_created date default sysdate not null)
organization index;

create sequence new_table_seq nocache;

create index new_table_prnt_id_dt_crtd_idx
    on new_table (parent_id, date_created)
        compress 1;

A lot less typing goes into the co_Table implementation. As mentioned previously, co_Table will handle naming entities to make sure they are Oracle compliant as well as standardized according to IOTK.

co_Table supports all of the features you would need to create tables in the IOTK framework including multi-column primary and foreign keys, check constraints, LOB columns, unique constraints, etc. In addition, it defines a number of functions for IOTK defined “data types” – ways to validate and represent data throughout the IOTK framework – like booleans, passwords, email addresses, and IOTK specific types like DataStore and Content IDs. Many of these data types include not just column definitions but constraint definitions as well for ensuring the data meets the integrity requirements set for them.

One missing piece is how you might use co_Table to actually load your SQL. It seems silly to have to create a PHP file than execute a script to create SQL that you would cut and paste into SQL*Plus. In fact, none of that happens when you’re using IOTK. One of the core facilities (and perhaps the most important part of the system) is the DB Builder. It automates the ordered tear down and rebuilding of all of the database objects defined by your application. All you have to do is create your SQL and put it in the right place and the DB Builder will handle finding and loading it or detecting that it has changed and reloading it. More on the DB Builder some time in the near future.

I have yet to deploy the Table Builder into a real product yet as it is currently the project I’m actively developing. I have high hopes for it and should have it completed in the next few days. In the mid-term I will investigate how to modify IOTK (and facilities like the Table Builder) to be able to produce either Oracle database code or, say, MySQL database code based on a configuration setting. This is a potentially powerful feature that is enabled by abstracting how the database entities are defined (described) and how they are actually written.