24 September, 2009

Mplayer Swiss Army Knife

Mplayer adopts the 'can do' attitude as a media player. Generally, this utility will play most anything, employs most standard codecs and many that you'll likely never need. If you have a video file, likely you can play it with Mplayer...including raw data.

A recent program I was working on grabbed raw LVDS data streams from a Flir infared camera. First task, confirm the video output is reasonable. Cue investigation of how to play raw data streams without requiring encoding. Mplayer met this need quite nicely. For fun, I thought I'd post some of our findings, hopefully you can find them useful.

Lets start with generating our own raw video stream:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <fcntl.h>
#include <assert.h>

int main()
{
static const int NumFrames = 10;
static const int Width = 640;
static const int Height = 480;

printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
std::string fileName="/tmp/video.raw";
printf("(%s:%d) fileName='%s'\n",__FILE__,__LINE__,fileName.c_str());
int fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);

for (int i=0; i<NumFrames; ++i)
{
unsigned char frame[Width*Height];

// generate frame; white box, framed with black lines
memset (frame,255,Width*Height);
for(int c=0; c<Width; ++c) frame[c+Width*0]=0;
for(int c=0; c<Width; ++c) frame[c+Width*(Height-1)]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h+(Width-1)]=0;

// write frame contents to file
for(int k=0; k<Width*Height; ++k)
{
void* tmp=(void*)(frame+k);
int ret=write(fp,tmp,sizeof(unsigned char));
assert(ret==sizeof(unsigned char));
}
}

close(fp);

printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}


The above code simply generates a sequence of 10 640x480 8-bit grayscale frames, the content of each frame a white background framed with a black one-pixel wide border.

You can play the video contents of this file by issuing the following Mplayer command:

$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307200:fps=1 /tmp/video.raw

A few things worth mentioning; first, since this is a raw video the frame size must be specified (ie. w=640:h=480), the frame rate (fps=1) must be specified as well or it will default to 24fps, giving us little time to review the contents of the video. Lastly, the size=307200 (640*480) is optional at for this format, but will become relevant in later examples. By default, the size will be defined by the width*height, so for this example you could simply not specify the value and all would still be well.

That was fun, but let's give it a little more snap. Suppose you wanted to add some frame metadata following each frame. We'll modify our above example a bit, each frame will be followed by a fixed 28-byte character string identifying the frame number.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <fcntl.h>
#include <assert.h>

int main()
{
static const int NumFrames = 10;
static const int Width = 640;
static const int Height = 480;

printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
std::string fileName="/tmp/video.raw";
printf("(%s:%d) fileName='%s'\n",__FILE__,__LINE__,fileName.c_str());
int fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);

for (int i=0; i<NumFrames; ++i)
{
unsigned char frame[Width*Height];

// generate frame; white box, framed with black lines
memset (frame,255,Width*Height);
for(int c=0; c<Width; ++c) frame[c+Width*0]=0;
for(int c=0; c<Width; ++c) frame[c+Width*(Height-1)]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h+(Width-1)]=0;

// write frame contents to file
for(int k=0; k<Width*Height; ++k)
{
void* tmp=(void*)(frame+k);
int ret=write(fp,tmp,sizeof(unsigned char));
assert(ret==sizeof(unsigned char));
}
//write some metadata
char metaData[28];
sprintf(metaData,"frame%04d",i);
assert(write(fp,metaData,sizeof(metaData))==sizeof(metaData));
}

close(fp);

printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}
Now the frame size becomes a required argument, since the frame size must now account for the metadata, otherwise the metadata would be interpreted as video and you'd have a mess. You can play the above generated video by issuing the command as follows:

$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307228:fps=1 /tmp/video.raw

Ok, now we've learned how to play video with metadata. Our last trick will be playing video with a initial video header.

Let's start by adding some video metadata at the beginning of the video, a 200-byte string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <fcntl.h>
#include <assert.h>

int main()
{
static const int NumFrames = 10;
static const int Width = 640;
static const int Height = 480;

printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
std::string fileName="/tmp/video.raw";
printf("(%s:%d) fileName='%s'\n",__FILE__,__LINE__,fileName.c_str());
int fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);

char videoMetaData[200];
sprintf(videoMetaData,"some cool video");
assert(write(fp,videoMetaData,sizeof(videoMetaData))==sizeof(videoMetaData));

for (int i=0; i<NumFrames; ++i)
{
unsigned char frame[Width*Height];

// generate frame; white box, framed with black lines
memset (frame,255,Width*Height);
for(int c=0; c<Width; ++c) frame[c+Width*0]=0;
for(int c=0; c<Width; ++c) frame[c+Width*(Height-1)]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h+(Width-1)]=0;

// write frame contents to file
for(int k=0; k<Width*Height; ++k)
{
void* tmp=(void*)(frame+k);
int ret=write(fp,tmp,sizeof(unsigned char));
assert(ret==sizeof(unsigned char));
}
//write some metadata
char metaData[28];
sprintf(metaData,"frame%04d",i);
assert(write(fp,metaData,sizeof(metaData))==sizeof(metaData));
}

close(fp);

printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}


You play this form of video by specifying a start byte offset of 200 and you're set.

$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307228:fps=1 -sb 200 /tmp/video.raw

20 September, 2009

Streaming with FFMpeg

For work I've been playing with VLC to support some streaming experiments for the program I'm working on. In the process of installing VLC it became obvious the vast number of package dependencies it's reliant on.

So, for fun, I played with streaming via FFMpeg at home. Below are my findings.

Assuming you have FFMpeg already installed on your machine, there are 3 steps:

1) start the ffserver application with appropriate configuration file.
2) start ffmpeg to redirect the input to the stream
3) view with video client

FFServer Setup
The ffserver utility is installed as part of the FFMpeg package, no need to install it explicitly. You do however need to author an appropriate configuration file. Below is the configuration file I've used as a starting point.

$ cat ffserver.conf
Port 8090
# bind to all IPs aliased or not
BindAddress 0.0.0.0
# max number of simultaneous clients
MaxClients 10
# max bandwidth per-client (kb/s)
MaxBandwidth 10000
# Suppress that if you want to launch ffserver as a daemon.
NoDaemon


File /tmp/feed1.ffm
FileMaxSize 5M


# FLV output - good for streaming

# the source feed
Feed feed1.ffm
# the output stream format - FLV = FLash Video
Format flv
VideoCodec flv
# this must match the ffmpeg -r argument
VideoFrameRate 10
# generally leave this is a large number
VideoBufferSize 80000
# another quality tweak
VideoBitRate 200
# quality ranges - 1-31 (1 = best, 31 = worst)
VideoQMin 30
VideoQMax 31
VideoSize 352x288
# this sets how many seconds in past to start
PreRoll 0
# wecams don't have audio
Noaudio


# ASF output - for windows media player

# the source feed
Feed feed1.ffm
# the output stream format - ASF
Format asf
VideoCodec msmpeg4
# this must match the ffmpeg -r argument
VideoFrameRate 10
# generally leave this is a large number
VideoBufferSize 80000
# another quality tweak
VideoBitRate 200
# quality ranges - 1-31 (1 = best, 31 = worst)
VideoQMin 30
VideoQMax 31
VideoSize 352x288
# this sets how many seconds in past to start
PreRoll 0
# wecams don't have audio
Noaudio

$


Next, you need to run ffserver as follows:

$ ffserver -f ffserver.conf


FFMpeg Setup
The ffmpeg utility feeds the server. The following example uses a quick cam as the source, redirecting through the video server to be viewed by the clients. Worth noting, the ffserver configuration file specifies a frame rate, in this case 10fps, which must match the command line specification of ffmpeg.


$ ffmpeg -r 10 -s 352x288 -f video4linux -i /dev/video0 http://localhost:8090/feed1.ffm



Video Client Setup
Lastly, test the feed using a video client. For our example, we'll use mplayer as follows:

$ mplayer http://localhost:8090/test.asf

Play nice.

09 September, 2009

Redirecting Sound with rDesktop

I've been working with VirtualBox for the past few months. I've grown to love it, and consider virtualization as one of the most compelling technologies we've seen in the past decade.

Running VMs headless has quite a few advantages. For instance, we're bundling X VMs on a server and serving out to less capable processors via RDP clients. The latest challenge was redirecting sound to the client processor. Like most things, pretty simple to do...once you've done it.

Thought it was worth a quick note:

Assuming you've started a VM with remote display enabled, such as:

$ VBoxHeadless -startvm WinXp -vrdp on -vrdpport 8000


To establish an RDP connection (assuming host IP of 192.168.1.27):


$ rdesktop 192.168.1.27:8000



$ rdesktop 192.168.1.27:8000 -r sound:local:oss

08 September, 2009

Older Debian Releases

Perhaps I'm living up to my name, the fat slow kid, but if I'm honest I have a hell of a time locating previous releases of Debian.

I search and I search and I search some more; after finally finding them I thought it was worth a quick post.

http://www.debian.org/releases/

For 'etch', follow the links to:
http://www.debian.org/releases/etch/debian-installer/

With the common redirects pressing you toward the latest-n-greatest sometimes its difficult to get the older version you're after.

Cheers.

21 August, 2009

VirtualBox - Restoration Media Installation

A curiosity I found was I have had success using OS restoration disks to create virtual machines. In particular a government program restoration disc used to reimage laptops, I can't get into details.

In order to make this work however I found I needed to enable passthrough on the CD/DVD drive, otherwise it hung prior to detecting the media.

Just a note, hope someone finds it valuable.

Multiple RDP Connections with VirtualBox

I was noodling through the latest VirtualBox 3.0.4 Manual and found the solution to a problem I've been chasing for now 2 months.

We've been trying to incorporate multiple user connections to a virtual machine to allow desktop sharing. The intention was the users would need to coordinate actions and share the desktop. The primary use was to support remote monitoring of a user's session while allowing the either user to interact as required.

VirtualBox 3.0.4 introduced a new vrdpmulticon specifier that appears to have addressed the issue.

From the command line you need to issue the following command to enable multiple RDP connection sharing, the effect is to allow multiple connections share a RDP server session.


$ VBoxManage modifyvm WinXp --vrdp on
$ VBoxManage modifyvm WinXp --vrdpmulticon on

Where in this case the name of the virtual machine is WinXp.

I've struggled with this for the past couple months. The solutions, 'til now, have been restricted to 'hacking WinXp' (a legal mine field) or upgrading to server 200x.

Hope you find this as useful as I do.
Cheers.

20 August, 2009

Capturing Video From QuickCam Express Using FFMpeg

Another quick post.

You can capture motion video from your QuickCam Express USB camera (assuming properly configured) by issuing the following command:


$ ffmpeg -r 15 -s 352x288 -f video4linux -i /dev/video0 /tmp/video.avi


This will capture at a 15 fps frame rate at the specified resolution.

Cheers.