#Copyright (c) 2008, Media Modifications Ltd. #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal #in the Software without restriction, including without limitation the rights #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the Software is #furnished to do so, subject to the following conditions: #The above copyright notice and this permission notice shall be included in #all copies or substantial portions of the Software. #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. # This class is a cut-down version of glive which uses an ximagesink # rather than an xvimagesink. This is used in video playback mode, where # our only Xv port is used for Theora playback. # # I tried to modify the glive pipeline to allow swapping an xvimagesink for # an ximagesink and vice-versa, but that didn't work out (all kinds of strange # behaviour, perhaps a gstreamer bug). So we resort to using a separate # pipeline - ugly, but it works... import os import gtk import pygtk pygtk.require('2.0') import sys import gst import gst.interfaces import pygst pygst.require('0.10') import time import threading import gobject gobject.threads_init() from instance import Instance from constants import Constants import record import utils import ui import glive class GliveX: def __init__(self, pca): self.VIDEO_FRAMERATE_SMALL = 10 self.window = None self.ca = pca self.playing = False self.pipeline = gst.Pipeline("slow-pipeline") self.createPipeline() bus = self.pipeline.get_bus() bus.enable_sync_message_emission() bus.add_signal_watch() self.SYNC_ID = bus.connect('sync-message::element', self._onSyncMessageCb) self.MESSAGE_ID = bus.connect('message', self._onMessageCb) def createPipeline ( self ): src = gst.element_factory_make("v4l2src", "camsrc") try: # old gst-plugins-good does not have this property src.set_property("queue-size", 2) except: pass # if possible, important to place the framerate limit directly on the # v4l2src so that it gets communicated all the way down to the camera # level if glive.can_limit_framerate: srccaps = gst.Caps('video/x-raw-yuv,framerate='+str(self.VIDEO_FRAMERATE_SMALL)+'/1') else: srccaps = gst.Caps('video/x-raw-yuv') # the XO-1.5 camera framerate limit increases image quality but # still delivers way too many frames. add a gstreamer-level filter # to keep the frame rate sensible. rate = gst.element_factory_make("videorate", "vbrate") ratecaps = gst.Caps('video/x-raw-yuv,framerate='+str(self.VIDEO_FRAMERATE_SMALL)+'/1') queue = gst.element_factory_make("queue", "dispqueue") queue.set_property("leaky", True) queue.set_property('max-size-buffers', 1) scale = gst.element_factory_make("videoscale", "scale") scalecaps = gst.Caps('video/x-raw-yuv,width='+str(ui.UI.dim_PIPW)+',height='+str(ui.UI.dim_PIPH)) colorspace = gst.element_factory_make("ffmpegcolorspace", "colorspace") xsink = gst.element_factory_make("ximagesink", "xsink") # http://thread.gmane.org/gmane.comp.video.gstreamer.devel/29644 xsink.set_property("sync", False) self.pipeline.add(src, rate, queue, scale, colorspace, xsink) src.link(rate, srccaps) rate.link(queue, ratecaps) queue.link(scale) scale.link(colorspace, scalecaps) colorspace.link(xsink) def play(self): self.pipeline.set_state(gst.STATE_PLAYING) self.playing = True def pause(self): self.pipeline.set_state(gst.STATE_PAUSED) self.playing = False def stop(self): self.pipeline.set_state(gst.STATE_NULL) self.playing = False def is_playing(self): return self.playing def idlePlayElement(self, element): element.set_state(gst.STATE_PLAYING) return False def _onSyncMessageCb(self, bus, message): if message.structure is None: return if message.structure.get_name() == 'prepare-xwindow-id': self.window.set_sink(message.src) message.src.set_property('force-aspect-ratio', True) def _onMessageCb(self, bus, message): t = message.type if t == gst.MESSAGE_EOS: #print("MESSAGE_EOS") pass elif t == gst.MESSAGE_ERROR: #todo: if we come out of suspend/resume with errors, then get us back up and running... #todo: handle "No space left on the resource.gstfilesink.c" #err, debug = message.parse_error() pass class SlowLiveVideoWindow(gtk.Window): def __init__(self, bgd ): gtk.Window.__init__(self) self.imagesink = None self.glivex = None self.modify_bg( gtk.STATE_NORMAL, bgd ) self.modify_bg( gtk.STATE_INSENSITIVE, bgd ) self.unset_flags(gtk.DOUBLE_BUFFERED) self.set_flags(gtk.APP_PAINTABLE) def set_glivex(self, pglivex): self.glivex = pglivex self.glivex.window = self def set_sink(self, sink): if (self.imagesink != None): assert self.window.xid self.imagesink = None del self.imagesink self.imagesink = sink self.imagesink.set_xwindow_id(self.window.xid)