Hello all, Chris,
This is a followup to the e-mail/code I sent in November:
http://lists.laptop.org/pipermail/sugar/2007-November/003832.html
The attached patch adds a library/pippy/camera.py module that is used
by two new scripts, data/graphics/showVideo and showDelayedVideo,
which show live video and 3-second-delayed video respectively. Using
the camera.py module, it shouldn't be too hard to add a timeLapse
script that actually stores photos in the Journal, especially neat
when combined with the slideShow script.
What do you think of always inserting "import pippy" into the copied
version of all scripts (thereby removing yet more not-helpful LOC)?
Regards,
Gabriel
diff --git a/data/graphics/showDelayedVideo b/data/graphics/showDelayedVideo
new file mode 100644
index 0000000..69d409b
--- /dev/null
+++ b/data/graphics/showDelayedVideo
@@ -0,0 +1,7 @@
+import pippy
+
+videoCam = pippy.camera.VideoCamera ()
+videoCam.show_window ()
+
+# Delay the camera output by 3 seconds.
+videoCam.set_delay (3)
diff --git a/data/graphics/showVideo b/data/graphics/showVideo
new file mode 100644
index 0000000..ddbce1c
--- /dev/null
+++ b/data/graphics/showVideo
@@ -0,0 +1,4 @@
+import pippy
+
+videoCam = pippy.camera.VideoCamera ()
+videoCam.show_window ()
diff --git a/library/pippy/__init__.py b/library/pippy/__init__.py
index 5814865..8410eaf 100644
--- a/library/pippy/__init__.py
+++ b/library/pippy/__init__.py
@@ -1,6 +1,7 @@
"""Pippy standard library."""
import pippy.console as console
import pippy.game as pygame
+import pippy.camera as camera
try:
import pippy.sound as sound
except ImportError:
diff --git a/library/pippy/camera.py b/library/pippy/camera.py
new file mode 100755
index 0000000..d2cca0d
--- /dev/null
+++ b/library/pippy/camera.py
@@ -0,0 +1,274 @@
+"""Camera helpers for pippy."""
+import pygst
+pygst.require('0.10')
+import gst
+import time
+import gobject
+
+from inspect import *
+
+gobject.threads_init()
+
+import threading
+import gtk
+gtk.gdk.threads_init()
+
+class CameraKeepaliveThread (threading.Thread):
+ def run (self):
+ while len(Camera.cameras) > 0:
+ time.sleep (0.5)
+
+class VideoWindowThread (threading.Thread):
+ def __init__ (self, camera):
+ self.camera = camera
+ self.window = None
+ threading.Thread.__init__ ( self )
+ self.setDaemon (False)
+
+ def run (self):
+ self.window = gtk.Window ()
+ self.window.unset_flags (gtk.DOUBLE_BUFFERED)
+ self.window.set_flags (gtk.APP_PAINTABLE)
+ self.window.set_property("events", gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK)
+ self.window.connect ("button-press-event", self.press_cb)
+ self.window.connect ("key-press-event", self.press_cb)
+ self.window.show ()
+ self.window.maximize ()
+ gtk.main ()
+
+ def press_cb (self, widget, event):
+ self.window.hide ()
+ gtk.main_quit ()
+ self.camera.stop_display ()
+
+ def xid (self):
+ if self.window == None or self.window.window == None:
+ return None
+ return self.window.window.xid
+
+def print_bus_msg (bus, msg, xwindow_id = None):
+ if msg.type == gst.MESSAGE_STATE_CHANGED:
+ return gst.BUS_DROP
+
+ if msg.type == gst.MESSAGE_ERROR:
+ print "pipeline error:", msg
+
+ if msg.structure != None and msg.structure.get_name () == 'prepare-xwindow-id':
+ camera = None
+ for cam in Camera.cameras:
+ if cam.__class__ == VideoCamera and cam.xvimagesink == msg.src:
+ camera = cam
+
+ if camera == None:
+ print "camera not found for prepare-xwindow-id event!"
+ return gst.BUS_DROP
+
+ if not camera.show_in_gtkwindow:
+ print 'camera.show_in_gtkwindow = false'
+ return gst.BUS_DROP
+
+ video_thread = VideoWindowThread (camera)
+ video_thread.start ()
+ while video_thread.xid () == None: pass
+ time.sleep (0.025)
+ msg.src.set_xwindow_id (video_thread.xid ())
+ msg.src.set_property ('force-aspect-ratio', True)
+
+ return gst.BUS_DROP
+
+class Camera:
+ cameras = []
+ #pipeline = gst.parse_launch ('v4l2src ! ffmpegcolorspace name=origin ! tee name=tee')
+ pipeline = gst.parse_launch ('v4l2src name=v4l ! video/x-raw-yuv, framerate=30/1 ! ffmpegcolorspace name=origin ! tee name=tee')
+ pipeline.get_bus().set_sync_handler(print_bus_msg)
+ active_cameras = 0
+
+ # Class methods
+
+ def inc_active_cameras (self):
+ Camera.cameras.append (self)
+ if len(Camera.cameras) == 1:
+ CameraKeepaliveThread ().start ()
+ Camera.pipeline.set_state (gst.STATE_PLAYING)
+
+ def dec_active_cameras (self):
+ Camera.cameras.remove (self)
+ if len(Camera.cameras) == 0:
+ Camera.pipeline.set_state (gst.STATE_PAUSED)
+
+ # Instance methods
+
+ def __init__ (self):
+ self.queue = gst.element_factory_make ('queue')
+ self.videoflip = gst.element_factory_make ('videoflip')
+ self.elements = [self.queue, self.videoflip]
+ self.set_rotation (4)
+
+ # 0 = normal
+ # 1 = rotated right
+ # 2 = upside down
+ # 3 = rotated left
+ # 4 = mirrored (default)
+ def set_rotation (self, method):
+ self.videoflip.set_property ('method', method)
+
+ def turnOff (self):
+ prev_element = None
+ self.elements.reverse ()
+ for element in self.elements:
+ Camera.pipeline.remove (element)
+ if prev_element != None:
+ element.unlink (prev_element)
+ prev_element = element
+
+ Camera.pipeline.get_by_name ('tee').unlink (prev_element)
+ Camera.pipeline.set_state (gst.STATE_NULL)
+ Camera.cameras = []
+
+ # Private instance methods
+
+ def _attach_to_pipeline (self):
+ prev_element = Camera.pipeline.get_by_name ('tee')
+ for element in self.elements:
+ Camera.pipeline.add (element)
+ prev_element.link (element)
+ prev_element = element
+
+class StillCamera (Camera):
+ def __init__ (self):
+ Camera.__init__ (self)
+
+ self.queue.set_property ('leaky', True)
+ self.queue.set_property ('max-size-buffers', 1)
+
+ self.ffmpegcolorspace = gst.element_factory_make ('ffmpegcolorspace')
+ self.fakesink = gst.element_factory_make ('fakesink')
+ self.fakesink.set_property ('signal-handoffs', True)
+
+ self.elements.extend ([
+ self.ffmpegcolorspace, self.fakesink
+ ])
+
+ self._attach_to_pipeline ()
+ self.set_colorspace ('video/x-raw-rgb, depth=24, bpp=24, width=640, height=480')
+
+
+ def set_colorspace (self, colorspace):
+ #print 'Setting camera colorspace to', colorspace
+ self.ffmpegcolorspace.get_pad('src').set_caps(
+ gst.caps_from_string (colorspace)
+ )
+
+ def takePicture (self):
+ self.buffer = None
+ self.takePictureAsync (None)
+ while (self.buffer == None):
+ time.sleep (0.25)
+ return self.result
+
+ def takePictureAsync (self, callback):
+ self.pictureCallback = callback
+ self.probe_id = self.fakesink.get_pad ('sink').add_buffer_probe (self._bufferHandler, None)
+ self.inc_active_cameras ()
+
+ # private methods
+
+ def _bufferHandler (self, pad, buffer, data):
+ self.fakesink.get_pad ('sink').remove_buffer_probe (self.probe_id)
+ self.dec_active_cameras ()
+ self._pictureResult (buffer)
+
+ if self.pictureCallback is not None:
+ self.pictureCallback (self.result)
+
+ self.pictureCallback = None
+
+ def _pictureResult (self, buffer):
+ caps = buffer.caps
+ self.width = caps[0]["width"]
+ self.height = caps[0]["height"]
+ self.buffer = buffer
+ self.result = [self.buffer, self.width, self.height]
+
+class VideoCamera (Camera):
+ def __init__ (self):
+ Camera.__init__ (self)
+
+ ffmpegcolorspace = gst.element_factory_make ('ffmpegcolorspace')
+ #self.videoflip = gst.element_factory_make ('videoflip')
+ self.videorate = gst.element_factory_make ('videorate')
+ #self.alpha = gst.element_factory_make ('alpha')
+ #self.alpha.set_property ('', True)
+ self.fakesink = gst.element_factory_make ('fakesink')
+ self.xvimagesink = gst.element_factory_make ('xvimagesink')
+ self.xvimagesink.set_property('sync', False)
+ self.xvimagesink.set_property('handle-events', True)
+
+ #self.set_rotation (0)
+ self.fakesink.set_property ('signal-handoffs', True)
+ #self.set_colorspace ('video/x-raw-rgb,depth=24,bpp=24')
+
+ self.elements.extend ([
+ ffmpegcolorspace,
+ #self.videorate,
+ #self.videoflip,
+ #self.fakesink
+ #self.alpha,
+ self.xvimagesink
+ ])
+
+ self._attach_to_pipeline ()
+ self.xvimagesink.set_property('handle-events', True)
+
+ def set_delay (self, seconds):
+ seconds = int(seconds)
+ if seconds < 0 or seconds > 30:
+ print 'Delay must be greater than 0 and less than 30 seconds.'
+ return
+
+ print 'Setting delay of video camera to', seconds, 'seconds.'
+ mult = 4.
+ self.queue.set_property('max-size-time', int((seconds + 1.0) * 1000000000.0))
+ self.queue.set_property('max-size-bytes', int(mult*3225600. * (seconds + 1)))
+ self.queue.set_property('max-size-buffers', int(mult*7. * (seconds + 1)))
+ self.queue.set_property('min-threshold-time', int(seconds * 1000000000.0))
+
+ # frames_ps is frames per second
+ def set_framerate (self, frames_ps):
+ frames_ps = int(frames_ps)
+ if frames_ps != 30 and frames_ps != 15:
+ print 'Frames per second must be equal to 15 or 30.'
+ return
+
+ print 'Setting framerate of video camera to', frames_ps, 'frames per second.'
+ #Camera.pipeline.get_by_name ('v4l').get_pad('src').set_caps(
+ Camera.pipeline.get_by_name ('origin').get_pad('sink').set_caps(
+ gst.caps_from_string ('video/x-raw-yuv, framerate=' + str(frames_ps) + '/1')
+ )
+
+ def print_delay(self):
+ for prop in ['buffers', 'bytes', 'time']:
+ print prop, 'max:', self.queue.get_property('max-size-' + prop), 'curr:', self.queue.get_property('current-level-' + prop)
+
+ #def set_rotation (self, method):
+ #self.videoflip.set_property ('method', method)
+
+ def set_colorspace (self, colorspace):
+ self.fakesink.get_pad ('sink').set_caps (colorspace)
+
+ def show_window (self):
+ print "Starting video camera.\nClick or press any key to stop the camera."
+ self.show_in_gtkwindow = True
+ self.start_display ()
+
+ def start_display (self):
+ #self.pipeline.add (self.xvimagesink)
+ #self.elements[-1].link (self.xvimagesink)
+ #self.elements.add (self.xvimagesink)
+ self.inc_active_cameras ()
+
+ def stop_display (self):
+ #self.elements[-2].unlink (self.xvimagesink)
+ #self.pipeline.remove (self.xvimagesink)
+ #self.elements.remove (self.xvimagesink)
+ self.dec_active_cameras ()
_______________________________________________
Sugar mailing list
[email protected]
http://lists.laptop.org/listinfo/sugar