Python power
Since I started hacking with python a few months ago, I never stopped being amazed by its power.
Wanna display you webcam video on a very simple GTK+ app? Less than one hundred lines, thanks to pygst (gstreamer) and pygtk!
#!/usr/bin/env python
import sys, os
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst
class GTK_Main:
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Webcam-Viewer")
window.set_default_size(500, 400)
window.connect("destroy", gtk.main_quit, "WM destroy")
vbox = gtk.VBox()
window.add(vbox)
self.movie_window = gtk.DrawingArea()
vbox.add(self.movie_window)
hbox = gtk.HBox()
vbox.pack_start(hbox, False)
hbox.set_border_width(10)
hbox.pack_start(gtk.Label())
self.button = gtk.Button("Start")
self.button.connect("clicked", self.start_stop)
hbox.pack_start(self.button, False)
self.button2 = gtk.Button("Quit")
self.button2.connect("clicked", self.exit)
hbox.pack_start(self.button2, False)
hbox.add(gtk.Label())
window.show_all()
# Set up the gstreamer pipepile
self.player = gst.parse_launch ("v4l2src ! autovideosink")
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)
def start_stop(self, w):
if self.button.get_label() == "Start":
self.button.set_label("Stop")
self.player.set_state(gst.STATE_PLAYING)
else:
self.player.set_state(gst.STATE_NULL)
self.button.set_label("Start")
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)
self.button.set_label("Start")
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
self.player.set_state(gst.STATE_NULL)
self.button.set_label("Start")
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()
And here you are! I could have reduced even more the code by using Glade to design and dynamically load the UI.

(there is supposed to be an image instead of the blackbox. Anyone know how to screenshot a Xv window?)
« Brainstorm: how to involve new contributors?
Weekly pick of Brainstorm ideas »
Comments
Comment from nand
Time: April 26, 2008, 10:02 pm
ok thanks for the tip, I’ll try it once I get back my hand on a webcam!
Comment from Rich
Time: April 29, 2008, 2:00 am
I worked your code around a bit, and came up with a webcam viewer and recorder. It supports both high quality (i.e. full mjpeg recording, about 7GB/hr) and low quality (mpeg4, much much less) with audio and live preview while recording. The UVC drivers I have are kind of old and flaky, so it doesn’t work every time you hit record…sometimes a try or two are necessary…hopefully this is better with newer drivers or different hardware. I more than doubled your program, but you gave me a good start…thanks! Configuration settings are at the top of the source, and should be self explanatory. Here’s the code; hopefully wordpress won’t neuter it (if so, send me an e-mail, which should be visible by you, and I’ll send it along):
#!/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=”/home/rich/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()
Comment from nand
Time: April 29, 2008, 9:17 am
hey thanks, I’ll be sure to give a try and post your code! (I received it properly indented in my mailbox)
This could be the start of a gnome cheese remake ![]()
Comment from Rich
Time: April 25, 2008, 2:46 am
Using a compositing manager with a video driver that supports texture from pixmap (only nvidia that I know of) will let you take a screenshot…or pipe the output through ximagesink…that should work without a compositing manager. Here is the rendering pipeline I modified your code to use: “v4l2src queue-size=16 ! image/jpeg,width=640,framerate=30/1,rate=30 ! ffdec_mjpeg ! queue2 max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! ffmpegcolorspace ! ximagesink” and I could take screenshots with the contents of the window. Thanks for the script…it will prove useful!