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!
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!
There are 10 comments on this page. Add yours.
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. ;)
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
wow
Thanks! Should help with an ID Card Creation software.
good
thank
vi voglio scopare
i love sex je taime beucoup l amour
y es una figura que no me quiero perder
Thanks :)
0.0115s
.