March 25, 2009

Hacking MFserver

The Maximum T-8000 PVR is an interesting device; it is a consumer-grade product and comes with a custom Linux OS. Marusÿs is the proprietor of the server's source code (which I believe they can, as it uses libupnp, which is licensed under BSD), but they offer it for download. The server (version 0.0.1) needed some patching to compile on GCC 4. The fixes were relatively simple. The major problem was that VLC-0.9 does no longer accept EXTVLCOPTS in playlists, and so far MFserver had relied on this. Fortunately VLC has a powerful way to chain up the needed transcode and networking modules from a single command, so I got it working by defining a few class variables and using sprintf() to contruct the VLC command. This means I can open the MFclient on the television screen, browse the server's video collection with the remote, and select a title to play on the TV. The PVR receives MPEG2 TS stream, such as in DVB broadcasts. I had a couple of test videos; a) DVB recordings from the PVR itself (ts) and from Kaffeine (m2t) b) Youtube videos (flv) c) YLE videos (wmv) d) DVD container units (?) (vob) e) random AVIs from the internet Videos that were recorded on the PVR, transferred to a PC, and streamed back without transcoding provided expectedly the best result, in almost no skipping in audio or video. There should be no loss in the video or audio quality. The Youtube videos seem to generally work quite reasonably. Some videos have low bitrates and it is obvious on the TV screen. Some of the random AVIs pulled from the internet worked very well, others did not work at all. I haven't yet got any picture from Kaffeine recordings. After some experimenting I figured the DVB recordings should not be transcoded. FFmpeg does seem to do ”the wrong thing” when it is asked to transcode from MPEG2 to MPEG2, and actually does demux and re-encode! The transcode options should be set by MFserver and be based on the video file suffix. In Ruby this would be as simple as:
transcode = 
    case filename[/\.([^\.]+)$/,1]
    when 'ts'
      ''
    else
      'transcode{vcodec=mp2v,acodec=mpga,.....'
    end
However, MFserver is written in C (although the file suffix is .cpp). I haven't touched C in years. Since then I have learned to do some programming on Java, Bash, Perl, Ruby, JavaScript and Python (in that chronological order). These are high level, (mostly) object oriented languages. Now, I had to face char[], *char and the paradigm shift from higher level abstractions to byte-level processing of an array, trying to remember how pointers work and what happens when I return a local variable and so on. There was an option to switch to C++ and use the String class without including any additional headers but I was intrigued by solving this in C. After three hours of intense reading and experimenting with a simple testing script, I had a function that parsed the suffix from the filename *char.
void VLCMgr::parseSuffix(char *filename)
{
       char _suffix[5] = "";
       int len = strlen(filename);
       int i=0;
       int seek = len-6;
       int dotreached=0;
       // get suffix
       for ( ; seek <= len ; seek++) {
               // separator . not reached
               if ((filename[seek]=='.') && strlen(_suffix)==0) 
               {
                       dotreached=1;
                       _suffix[i] = filename[seek]; // this will be overwritten
               }
               else if ((dotreached==1) && strlen(_suffix)!=0)
               {
                       // then do not add dots
                       if (filename[seek]!='.')
                       {
                               _suffix[i] = filename[seek];
                               i++;
                       }
               }
       }
       filename[i-1]='\0'; // mark the end of suffix string
       strncpy(filename,_suffix,5);
}
That's a lot of work just to get such a simple task done. Remember the Ruby equivalent was
filename[/\.([^\.]+)$/,1]
It makes me even more amazed how people can program whole operating systems and desktop environments with this language. I also dug up example sources of socket communication and merged them in to send remote control commands to VLC's remote control interface that was opened on a TCP socket. This provided to be somewhat tricky but now the commands pause, faster and slower are sent to the VLC socket. However the speeding up function does not seem to work while transcoding. This is an ongoing project and I'll post updates and the patches later, when I get a reasonable set of functionality finished.

March 23, 2009

Notes on using multiple ALSA cards

The last few weeks have been quite exciting. I studied how to configure two ALSA chipsets to operate simultaneously. Then I worked on to bring a stable version of lportal-1.1, and hacked MFserver to stream video from a PC over the network to a PPC-based PVR device (Maximum T-8000). And learning the basics of git in the meanwhile! :) Lots of fun. My testbench was Sabayon-4 with Linux kernel 2.6.27, and had to switch from kernel ALSA to a newer version to get it working properly. After learning a bit about ALSA, it boiled down to configuring /etc/modprobe.d/alsa to define the card indices and their driver. I have a setup with integrated Intel audio which has altogether 5 analog 3.5" stereo audio plugs and a S/PDIF connector .. on the motherboard. I just today got a connector card for this, but I already had an M-AUDIO Audiophile 2496 that has a coax S/PDIF output available. I connected this with a cheap 1 m single RCA coax cable to Pioneer stereo system. After all the work of compiling ALSA headers for the correct kernel and hassling with the kernel modules, I got both cards working simultaneously. I could play the same audio file in two different processes and send the output to analog and digital simultaneously. Both input sources are selected by the "CD" switch on the amplifier, and a separate analog/digital button is available to select the source. Cool! To my ears it seems that the digital is crisp and clear, but suffers from constant static cracles during some tracks. Analog source is a bit muddier, but the dynamic range is somehow more pleasant for me, so I left the analog device the default output. I decided to use ~/.asoundrc to set the default output device: To set the Audiophile's digital output:
pcm.!default iec958:M2496
Or the integrated Intel analog:
pcm.!default front:Intel
Speaker-test is a good testing tool:
$ speaker-test -c2 -Diec958:CARD=M2496,DEV=0
 $ speaker-test -c2 -Dfront:CARD=Intel
I use Amarok 2 to play music, using Phonon and KDE-4.2.1. Works fine.