Python and webcam, part 2
A few weeks ago, I published a small webcam viewer script written in python using GTK+ and Gstreamer. Rich submitted in a comment some nice enhancements to the code while keeping it under 200 lines: the ability to record videos from your webcam, in high and low quality, and with sound! Good job!

That may be the start of a Cheese clone
Here is the code:
#!/usr/bin/env python
import sys, os, time
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst
BKGRNDR=0.0
BKGRNDG=0.2
BKGRNDB=0.7
BKGRNDA=0.8
WINDOW_STICK=False
WINDOW_KEEPABOVE=False
WINDOW_DECORATED=True
WIDTH=790
HEIGHT=480
TOP=100
LEFT=100
timeElapsed = 0
videoDevice="/dev/video0"
videoProperties="image/jpeg,width=640,height=480,framerate=30/1"
audioDevice="hw:1,0"
audioProperties="audio/x-raw-int,rate=16000,channels=1,depth=16"
videoSink="ximagesink"
filePath=os.path.expanduser("~") + '/Desktop'
mpeg4Properties=""
class GTK_Main:
def __init__(self):
global filePath, videoDevice, videoProperties, videoSink
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Video-Recorder")
window.set_size_request(WIDTH, HEIGHT)
window.set_decorated(WINDOW_DECORATED)
window.connect("destroy", gtk.main_quit, "WM destroy")
window.move(LEFT, TOP)
mainBox = gtk.HBox()
window.add(mainBox)
vbox = gtk.VBox()
mainBox.add(vbox)
self.movie_window = gtk.DrawingArea()
vbox.add(self.movie_window)
controlBox = gtk.VBox()
mainBox.pack_start(controlBox, False)
controlBox.set_size_request(150,480)
controlBox.set_border_width(10)
controlBox.pack_start(gtk.Label())
self.button = gtk.Button("Record (HQ)")
self.button.connect("clicked", self.start_stop_recordHQ)
controlBox.pack_start(self.button, False)
self.button1 = gtk.Button("Record (LQ)")
self.button1.connect("clicked", self.start_stop_recordLQ)
controlBox.pack_start(self.button1, False)
self.button3 = gtk.Button("Quit")
self.button3.connect("clicked", self.exit)
controlBox.pack_start(self.button3, False)
controlBox.add(gtk.Label())
self.ET = gtk.Label()
self.ET.set_line_wrap(True)
controlBox.pack_start(self.ET, False)
window.show_all()
self.player = gst.parse_launch ("v4l2src device="+videoDevice+" queue-size=16 ! "+videoProperties+" ! ffdec_mjpeg ! queue2 max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! ffmpegcolorspace ! "+videoSink)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect('message', self.on_message)
bus.connect('sync-message::element', self.on_sync_message)
self.start(firstRun = True)
self.recorderLQ = None
self.recorderHQ = None
def SetPipelines(self, Type):
global filePath, videoDevice, videoProperties, videoSink, audioDevice, audioProperties, mpeg4Properties
if Type == "LQ":
self.recorderLQ = gst.parse_launch ("v4l2src device="+videoDevice+" queue-size=16 ! "+videoProperties+" ! ffdec_mjpeg ! queue max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! queue ! tee name=tee tee. ! ffmpegcolorspace ! queue ! ffenc_mpeg4 ! queue ! mux. avimux name=mux alsasrc device="+audioDevice+" ! audiorate ! "+audioProperties+" ! queue ! audioconvert ! lame ! mux. mux. ! queue ! filesink location="+filePath+time.strftime("%Y%m%d%H%M%S", time.localtime()) +".avi tee. ! ffmpegcolorspace ! queue ! "+videoSink)
busrecLQ = self.recorderLQ.get_bus()
busrecLQ.add_signal_watch()
busrecLQ.enable_sync_message_emission()
busrecLQ.connect('message', self.on_message)
busrecLQ.connect('sync-message::element', self.on_sync_message)
self.recorderHQ = None
elif Type == "HQ":
self.recorderHQ = gst.parse_launch ("v4l2src device="+videoDevice+" queue-size=16 ! "+videoProperties+" ! tee name=tee tee. ! queue ! mux. avimux name=mux alsasrc device="+audioDevice+" ! audiorate ! "+audioProperties+" ! queue ! audioconvert ! lame ! mux. mux. ! queue ! filesink location="+filePath+time.strftime("%Y%m%d%H%M%S", time.localtime()) +".avi tee. ! ffdec_mjpeg ! queue max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! queue ! ffmpegcolorspace ! "+videoSink)
busrecHQ = self.recorderHQ.get_bus()
busrecHQ.add_signal_watch()
busrecHQ.enable_sync_message_emission()
busrecHQ.connect('message', self.on_message)
busrecHQ.connect('sync-message::element', self.on_sync_message)
self.busrecLQ = None
def start(self, firstRun = False):
if (firstRun == True):
time.sleep(1)
self.player.set_state(gst.STATE_PLAYING)
def start_stop_recordHQ(self, w):
global timeElapsed
if self.button.get_label() == "Record (HQ)":
self.SetPipelines("HQ")
timeElapsed = 0
self.button.set_label("Stop Recording")
self.player.set_state(gst.STATE_NULL)
if self.recorderLQ != None:
self.recorderLQ.set_state(gst.STATE_NULL)
time.sleep(1)
self.recorderHQ.set_state(gst.STATE_PLAYING)
self.button.set_sensitive(True)
self.button1.set_sensitive(False)
self.updateET()
else:
self.recorderHQ.set_state(gst.STATE_NULL)
time.sleep(1)
if self.recorderLQ != None:
self.recorderLQ.set_state(gst.STATE_NULL)
self.player.set_state(gst.STATE_PLAYING)
self.button.set_label("Record (HQ)")
self.button.set_sensitive(True)
self.button1.set_sensitive(True)
self.ET.set_label("")
def start_stop_recordLQ(self, w):
global timeElapsed
if self.button1.get_label() == "Record (LQ)":
self.SetPipelines("LQ")
timeElapsed = 0
self.button1.set_label("Stop Recording")
self.player.set_state(gst.STATE_NULL)
if self.recorderHQ != None:
self.recorderHQ.set_state(gst.STATE_NULL)
time.sleep(1)
self.recorderLQ.set_state(gst.STATE_PLAYING)
self.button1.set_sensitive(True)
self.button.set_sensitive(False)
self.updateET()
else:
self.recorderLQ.set_state(gst.STATE_NULL)
time.sleep(1)
if self.recorderHQ != None:
self.recorderHQ.set_state(gst.STATE_NULL)
self.player.set_state(gst.STATE_PLAYING)
self.button1.set_label("Record (LQ)")
self.button.set_sensitive(True)
self.button1.set_sensitive(True)
self.ET.set_label("")
def updateET(self):
global timeElapsed
self.ET.set_label(str(timeElapsed)+" sec.")
print str(timeElapsed)+" sec."
timeElapsed = timeElapsed + 1
if self.button.get_label() == "Stop Recording" or self.button1.get_label() == "Stop Recording":
gobject.timeout_add(1000, self.updateET)
else:
self.ET.set_label("")
def exit(self, widget, data=None):
gtk.main_quit()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.player.set_state(gst.STATE_NULL)
if self.recorderHQ != None:
self.recorderHQ.set_state(gst.STATE_NULL)
if self.recorderLQ != None:
self.recorderLQ.set_state(gst.STATE_NULL)
self.button.set_label("Record (HQ)")
self.button1.set_label("Record (LQ)")
self.button.set_sensitive(True)
self.button1.set_sensitive(True)
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
gobject.timeout_add(1000, self.ET.set_label, "Error: %s" % err)
self.player.set_state(gst.STATE_NULL)
if self.recorderHQ != None:
self.recorderHQ.set_state(gst.STATE_NULL)
if self.recorderLQ != None:
self.recorderLQ.set_state(gst.STATE_NULL)
self.button.set_label("Record (HQ)")
self.button1.set_label("Record (LQ)")
self.button.set_sensitive(True)
self.button1.set_sensitive(True)
def on_sync_message(self, bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
if message_name == 'prepare-xwindow-id':
# Assign the viewport
imagesink = message.src
imagesink.set_property('force-aspect-ratio', True)
imagesink.set_xwindow_id(self.movie_window.window.xid)
GTK_Main()
gtk.gdk.threads_init()
gtk.main()
« You know something must be going on…
New ways to handle third party software installations »
Comments
Comment from nand
Time: April 29, 2008, 8:56 pm
It should probably be quite easy. The most important part of all this code is the GStreamer pipeline, which is describing the actual processing of the webcam feed.
Here, this is a pretty large one that describe how to record the video and audio feed: “v4l2src device=”+videoDevice+” queue-size=16 ! “+videoProperties+” ! tee name=tee tee. ! queue ! mux. avimux name=mux alsasrc device=”+audioDevice+” ! audiorate ! “+audioProperties+” ! queue ! audioconvert ! lame ! mux. mux. ! queue ! filesink location=”+filePath+time.strftime(”%Y%m%d%H%M%S”, time.localtime()) +”.avi tee. ! ffdec_mjpeg ! queue max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! queue ! ffmpegcolorspace ! “+videoSink”
The one I used for the other script was much simpler : “v4l2src ! autovideosink”
You should take a look at http://maemo.org/development/documentation/how-tos/3-x/howto_camera_api_bora.html, the solution to your problem seems to be in the end of the page.
Comment from Rich
Time: May 5, 2008, 4:57 am
Hey…look at that! I’m glad it worked for someone besides myself! My webcam (well, just the PCB and lens…long story) caught fire and now shorts out 1/2 the USB root hubs when ever I plug it in, so sadly I haven’t gotten to mess with this much since I posted it up here. I have a new, better webcam on the way (see http://www.consumer.philips.com/consumer/en/ph/consumer/cc/_productid_SPC1300NC_00_PH_CONSUMER/Webcam+SPC1300NC-00 ), so I’ll get to play with this more in the future. Sadly, in my few weeks of messing with Python, I’ve come across 1 dead end that forced me to write in C, which I haven’t done in close to 10 years; it had to do with transmitting and receiving binary through a serial (ttyUSB) interface, and for what ever reason, Pyserial wasn’t up to the task. But for everything else I’ve used python for, I’ve been very impressed with how much I can accomplish in an afternoon. I’ll drop you a line if I end up expanding the scope of this script and hosting it on LP or sourceforge. Thanks for throwing this up here! And good catch with the os.path.expanduser(”~”).
-Rich
Comment from xerxes2
Time: May 7, 2008, 7:52 pm
Hello guys. Nice to see you’re using pygst and I’m wondering if I can put your examples in the pygst tutorial?
Greets Jens
Comment from nand
Time: May 7, 2008, 8:03 pm
Of course, for the first webcam example! But this example is from Rich, let’s wait for his answer.
Comment from xerxes2
Time: May 8, 2008, 7:16 pm
ok thanks. http://pygstdocs.berlios.de/pygst-tutorial/webcam-viewer.html
I don’t own a webcam myself so if you wanna make changes or submit some more text just mail me and I’ll put it right in.
Comment from Rich
Time: May 8, 2008, 10:52 pm
Please, feel free to include the recorder program in the pygst tutorial! Note that it is geared towards cameras that use mjpeg as opposed to raw yuv or other formats (most any webcam supported by the UVC driver, as well as several others, use mjpeg).
Comment from xerxes2
Time: May 11, 2008, 10:51 pm
ok thanks Rich. I got your mail with the updated version and will add it to the tutorial ASAP.
Comment from manatlan
Time: April 29, 2008, 8:15 pm
Nice script … til now, I used opencv in python to play with my v4l2 cam (see here : http://www.jperla.com/blog/2007/09/26/capturing-frames-from-a-webcam-on-linux/ )
I used it to save a frame as a jpeg … do you think it’s easy to hack your script to save only a frame instead of a movie (ex: take snapshot)
I really like you give me an simple example …