Enhanced demo.py

From PiTiViWiKi

Jump to: navigation, search
#!/usr/bin/env python

"""Basic Framework for writing GStreamer Demos in Python"""
#<excerpt 2>
import gobject
gobject.threads_init()
import gst
#</excerpt>
import pygtk
pygtk.require("2.0")
import gtk
gtk.gdk.threads_init()
import sys
import os


class DemoException(Exception):
    """Base exception class for errors which occur during demos"""
    
    def __init__(self, reason):
        self.reason = reason

class Demo:
    """Base class implementing boring, boiler-plate code.
    Sets up a basic gstreamer environment which includes:

    * a window containing a drawing area and basic media controls
    * a basic gstreamer pipeline using an ximagesink
    * connects the ximagesink to the window's drawing area
    
    Derived classes need only override magic(), __name__,
    and __usage__ to create new demos."""

    __name__ = "Enhanced Demo"
    __usage__ = "python demo.py -- runs a simple test demo"
    __def_win_size__ = (320, 240)

    # this commment allows us to include only a portion of the file
    # in the tutorial for this demo
    # <excerpt 1>     ...
    
    def magic(self, pipeline, sink, args):
        """This is where the magic happens"""
        src = gst.element_factory_make("videotestsrc", "src")
        pipeline.add(src)
        src.link(sink)

    def messageCb(self, bus, message):
        if message.type == gst.MESSAGE_STATE_CHANGED:
            old, new, pending = message.parse_state_changed()
            self.updateButtons(new)
        return gst.BUS_PASS
 
    def createPipeline(self, w):
        """Given a window, creates a pipeline and connects it to the window"""

        # code will make the ximagesink output in the specified window
        def set_xid(window):
	    gtk.gdk.threads_enter()
            sink.set_xwindow_id(window.window.xid)
            sink.expose()
            gtk.gdk.threads_leave()

        # this code receives the messages from the pipeline. if we
        # need to set X11 id, then we call set_xid
        def bus_handler(unused_bus, message):
            if message.type == gst.MESSAGE_ELEMENT:
                if message.structure.get_name() == 'prepare-xwindow-id':
                    set_xid(w)
            return gst.BUS_PASS

        # create our pipeline, and connect our bus_handler
        self.pipeline = gst.Pipeline()
        bus = self.pipeline.get_bus()
        bus.set_sync_handler(bus_handler)
        bus.add_watch(self.messageCb)

        sink = gst.element_factory_make("ximagesink", "sink")
        sink.set_property("force-aspect-ratio", True)
        sink.set_property("handle-expose", True)
        scale = gst.element_factory_make("videoscale", "scale")
        cspace = gst.element_factory_make("ffmpegcolorspace", "cspace")

        # our pipeline looks like this: ... ! cspace ! scale ! sink
        self.pipeline.add(cspace, scale, sink)
        scale.link(sink)
        cspace.link(scale)
        return (self.pipeline, cspace)
    
    # ... end of excerpt </excerpt>

    # subclasses can override this method to provide custom controls
    def customWidgets(self):
        return gtk.HBox()

    def createWindow(self):
        """Creates a top-level window, sets various boring attributes,
        creates a place to put the video sink, adds some and finally
        connects some basic signal handlers. Really, really boring.
        """

        # create window, set basic attributes
        w = gtk.Window()
        w.set_size_request(*self.__def_win_size__)
        w.set_title("Gstreamer " + self.__name__)
        w.connect("destroy", gtk.main_quit)

        # declare buttons and their associated handlers
        controls = (
            ("play_button", gtk.ToolButton(gtk.STOCK_MEDIA_PLAY), self.onPlay),
            ("pause_button", gtk.ToolButton(gtk.STOCK_MEDIA_PAUSE), self.onPause),
            ("stop_button", gtk.ToolButton(gtk.STOCK_MEDIA_STOP), self.onStop),
            ("quit_button", gtk.ToolButton(gtk.STOCK_QUIT), gtk.main_quit)
        )

        # as well as the container in which to put them
        box = gtk.HButtonBox()

        # for every widget, connect to its clicked signal and add it
        # to the enclosing box
        for name, widget, handler in controls:
            widget.connect("clicked", handler)
            box.pack_start(widget, True)
            setattr(self, name, widget)
        self.updateButtons(gst.STATE_NULL)
        
        viewer = gtk.DrawingArea()
        viewer.modify_bg(gtk.STATE_NORMAL, viewer.style.black)
        
        # we will need this later
        self.xid = None
        
        # now finally do the top-level layout for the window
        layout = gtk.VBox(False)
        layout.pack_start(viewer)

        # subclasses can override childWidgets() to supply
        # custom controls
        layout.pack_start(self.customWidgets(), False, False)
        layout.pack_end(box, False, False)
        w.add(layout)
        w.show_all()

        # we want to return only the portion of the window which will
        # be used to display the video, not the whole top-level
        # window. a DrawingArea widget is, in fact, an X11 window.
        return viewer

    def onPlay(self, unused_button):
        self.pipeline.set_state(gst.STATE_PLAYING)

    def onPause(self, unused_button):
        self.pipeline.set_state(gst.STATE_PAUSED)

    def onStop(self, unused_button):
        self.pipeline.set_state(gst.STATE_READY)

    def updateButtons(self, state):
        if state == gst.STATE_NULL:
            self.play_button.set_sensitive(True)
            self.pause_button.set_sensitive(False)
            self.stop_button.set_sensitive(False)
        elif state == gst.STATE_READY:
            self.play_button.set_sensitive(True)
            self.pause_button.set_sensitive(False)
            self.stop_button.set_sensitive(False)
        elif state == gst.STATE_PAUSED:
            self.play_button.set_sensitive(True)
            self.pause_button.set_sensitive(False)
            self.stop_button.set_sensitive(True)
        elif state == gst.STATE_PLAYING:
            self.play_button.set_sensitive(False)
            self.pause_button.set_sensitive(True)
            self.stop_button.set_sensitive(True)
    
    def run(self):
        w = self.createWindow()
        p, s = self.createPipeline(w)
        try:
            self.magic(p, s, sys.argv[1:])
            gtk.main()
        except DemoException, e:
            print e.reason
            print self.__usage__
            sys.exit(-1)

# if this file is being run directly, create the demo and run it
if __name__ == '__main__':
    Demo().run()
Personal tools