/home/nand

Entries Comments


Python power

4 April, 2008 (13:13) | Linux

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.

webcam-viewer.png

(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 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!

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 :)

Write a comment