Webcam Streaming in Perl/Tk (Linux)

September 2, 2009 by Noah

Late last week I started thinking about how to access a webcam device from within Perl. I have no direct need of such capability at the time being but I wanted to know how to do it in case I wanted to do something in the future involving webcams.

A few years ago when I used mostly Windows I found EZTwain, a DLL library for accessing a webcam in Windows using the TWAIN protocol (which as I understand is obsolete by now). The DLL was a pain in the butt to use and I couldn't get it to work how I wanted it to (it insisted on displaying its own GUI windows instead of allowing my Perl script to directly pull a frame from it without a GUI).

Besides that there's pretty much no libraries Perl has been built to use yet that can access a webcam. So, I started looking into using third-party programs such as ffmpeg and mplayer/mencoder to provide the hardware layer for me so that Perl can get just the jpeg images out and do with them what it needs.

Of these programs I wanted to use ffmpeg the most, because I know for sure there's an ffmpeg.exe for Windows, which might mean that whatever code I come up with might be reasonably portable to Windows as well.

After some searching I found some command-line sorcery for using ffmpeg over SSH to activate the camera on a remote computer and stream the video from it over SSH to the local system, and display it in mplayer:

ssh user@remoteip ffmpeg -b 100K -an -f video4linux2 -s 320x240 -r 10 -i /dev/video0 -b 100K -f ogg - | mplayer - -idle -demuxer ogg

Using the basic ffmpeg command in there, along with some hours of research and poking around, I eventually came up with a command that would activate the webcam and output a ton of jpeg images with consecutive file names, of each frame of video that the camera recorded:

ffmpeg -b 100K -an -f video4linux2 -s 640x480 -r 10 -i /dev/video0 -b 100K -f image2 -vcodec mjpeg test%d.jpg

The mjpeg codec (or "motion jpeg"), in ffmpeg, really means it's a bunch of jpeg images all combined together one after the other (the start of each jpeg image can be seen in hex by looking for the magic number, 0xFFD8). The "image2" format here means that each frame from the mjpeg stream gets written to an individual image file, in the format test%d.jpg where %d is a number that goes up for each image written.

By changing the image2 to image2pipe instead, the output (all the jpeg images in the mjpeg stream) is sent through the program's standard output, so it can be piped into another program, or read from in Perl.

So in Perl I opened a pipe that executes this command and have the script read from it, reading all the jpeg images and then displaying them in a Perl/Tk window as they come in. In effect: a live webcam stream, where Perl is entirely in control of the jpegs as they come in from ffmpeg and can do with them whatever it wants!

Tk Stream

I added a button to my GUI for taking a snapshot and saving it to disk (in actuality, as each complete image is read and displayed, it's kept around in memory until the next image is read and displayed... so this button just saves the last full image to disk).

Here's my proof of concept Perl code:

#!/usr/bin/perl -w

# Perl/Tk Webcam Streamer and Snapshot Taker
# Proof of Concept
# Author: Casey Kirsle, http://www.cuvou.com/

use Tk;
use Tk::JPEG;
use MIME::Base64 "encode_base64";

# Some things that might need to be configured.
my $device = shift(@ARGV) || "/dev/video0";
if ($device =~ /^\// && !-e $device) {
    die "Can't see video device: $device";
}

# Tk MainWindow
my $mw = MainWindow->new (
    -title => 'Tk Stream',
);
$mw->protocol (WM_DELETE_WINDOW => \&onExit);

# A label to display the photos.
my $photo = $mw->Label ()->pack();

# A button to capture a photo
my $capture = $mw->Button (
    -text => "Take Picture",
    -command => \&snapshot,
)->pack();

$mw->update();

You can download it here. It should run on any Linux distribution and it depends on having Perl/Tk and ffmpeg installed, and the video4linux2 system (any modern distro will have that).

In the ffmpeg command here you'll see I also piped the output into a quick Perl script that substitutes all the jpeg headers so that they begin with "KIRSLESEP" -- this was to make it easier to split the jpegs up while reading from the stream.

Since this uses ffmpeg and there's an ffmpeg.exe for Windows, this might work on Windows (you'll definitely need to modify the arguments sent to the ffmpeg command, though). I don't currently have access to a Windows machine with a webcam, though, so I can't work on that just yet.

Anyway, here it is: webcam access in Perl!

Tags:

Comments

There are 10 comments on this page. Add yours.

Avatar image
Noah (@kirsle) posted on September 28, 2009 @ 21:21 UTC

To fix the terminal afterward, just use the "reset" command.

It seems to be that ffmpeg changes the readmode on the terminal; I could fix it in my script with Term::ReadKey so it fixes the mode just before exiting... but this script is just a proof of concept anyway and I don't feel like fixing it. ;)

Avatar image
Anonymous posted on April 2, 2010 @ 16:41 UTC

error in accessing webcam stream using ffmpeg... when i run your code or write ffmpeg -f video4linux2 -s 720x480 -r 30000/1001 -i /dev/video1 -f avi -vcodec mjpeg -s cif -r 30000/1001 -y silver.avi

on terminal ... it gves error ,

[video4linux2 @ 0x99e0420][3]Capabilities: 5000001 [video4linux2 @ 0x99e0420]The V4L2 driver changed the video from 720x480 to 352x288 [video4linux2 @ 0x99e0420]Cannot find a proper format for codec_id 0, pix_fmt -1. /dev/video1: Input/output error

i knw the /dev/vid1 path is correct.... what is this error codec _id=0 cannot find proper format?? plz explain how to fix this?ion

my email is sarosh.nust@hotmail.com plzzz help i want a quick solution

Avatar image
caitlyn posted on July 12, 2010 @ 04:59 UTC

wow

Avatar image
Andrew Dougherty posted on July 31, 2010 @ 16:55 UTC

Thanks! Should help with an ID Card Creation software.

Avatar image
go0t0oyo0 posted on November 16, 2010 @ 07:09 UTC

good

Avatar image
gootooyoo posted on November 17, 2010 @ 12:15 UTC

thank

Avatar image
yutren posted on April 22, 2014 @ 09:05 UTC

vi voglio scopare

Avatar image
billel posted on April 29, 2014 @ 18:27 UTC

i love sex je taime beucoup l amour

Avatar image
sebastian duerte posted on May 15, 2014 @ 03:40 UTC

y es una figura que no me quiero perder

Avatar image
Anonymous posted on June 22, 2016 @ 19:20 UTC

Thanks :)

Add a Comment

Used for your Gravatar and optional thread subscription. Privacy policy.
You may format your message using Markdown syntax.