/home/nand

Entries Comments


Python and webcam, part 2

29 April, 2008 (18:37) | Linux

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!

videorecorder.jpg

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 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 …

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.

Write a comment