--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.10 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-connect</property>
+ </object>
+ <object class="GtkWindow" id="mainwindow">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">GStreamer video test</property>
+ <property name="icon_name">video-display</property>
+ <child>
+ <object class="GtkVBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="viewport">
+ <property name="width_request">1280</property>
+ <property name="height_request">720</property>
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="yalign">1</property>
+ <child>
+ <object class="GtkHButtonBox" id="buttonbox1">
+ <property name="height_request">46</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">8</property>
+ <property name="homogeneous">True</property>
+ <property name="layout_style">center</property>
+ <child>
+ <object class="GtkButton" id="play">
+ <property name="label">Stream</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">image1</property>
+ <signal name="clicked" handler="on_stream_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="stop">
+ <property name="label">gtk-media-stop</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_stop_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="quit">
+ <property name="label">gtk-quit</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_quit_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
--- /dev/null
+#!/usr/bin/env python
+# GdkX11 to get access to xid, GstVideo to get access to set_window_handle
+import gi
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gst', '1.0')
+gi.require_version('GstVideo', '1.0')
+from gi.repository import Gtk, Gst, GstVideo, GdkX11
+import signal
+
+settings = Gtk.Settings.get_default()
+settings.props.gtk_button_images = True
+
+class Main:
+ def __init__(self):
+
+ # Create gui bits and bobs
+
+ self.mainwindow = Gtk.Builder()
+ self.mainwindow.add_from_file("stream.glade")
+
+ signals = {
+ "on_stream_clicked" : self.OnStream,
+ "on_stop_clicked" : self.OnStop,
+ "on_quit_clicked" : self.OnQuit,
+ }
+
+ self.mainwindow.connect_signals(signals)
+
+ # Create GStreamer bits and bobs
+
+ # Initiate the pipeline
+ Gst.init(None)
+ self.pipeline = Gst.Pipeline("mypipeline")
+
+ # Add our video source, the first v4l2 device we can find
+ self.videosrc = Gst.ElementFactory.make("v4l2src", "videosource")
+ self.pipeline.add(self.videosrc)
+
+ # Add a jpeg decoder to force the v4l2src to output jpeg data
+ self.jpegdec = Gst.ElementFactory.make("jpegdec", "jpegdec")
+ self.pipeline.add(self.jpegdec)
+ self.videosrc.link(self.jpegdec)
+
+ # Add some video convert magic
+ self.videoconvert = Gst.ElementFactory.make("videoconvert", "videoconvert")
+ self.pipeline.add(self.videoconvert)
+ self.jpegdec.link(self.videoconvert)
+
+ # Add a videosink element to the pipeline
+ self.videosink = Gst.ElementFactory.make("autovideosink", "videosink")
+ self.videosink.set_property("sync", False)
+ self.pipeline.add(self.videosink)
+ self.videoconvert.link(self.videosink)
+
+ # Start playing
+ self.pipeline.set_state(Gst.State.PLAYING)
+
+ # Set up a bus to our pipeline to get notified when the video is ready
+ self.bus = self.pipeline.get_bus()
+ self.bus.enable_sync_message_emission()
+ self.bus.connect("sync-message::element", self.OnSyncElement)
+
+ # Summon the window and connect the window's close button to quit
+ self.window = self.mainwindow.get_object("mainwindow")
+ self.window.connect("delete-event", Gtk.main_quit)
+ self.window.show_all()
+
+ # Get window ID of the viewport widget from the GUI
+ self.win_id = self.mainwindow.get_object("viewport").get_window().get_xid()
+
+
+ # When we get a message that video is ready to display, set the
+ # correct window id to hook it to our viewport
+ def OnSyncElement(self, bus, message):
+ if message.get_structure().get_name() == "prepare-window-handle":
+ print "prepare-window-handle"
+ message.src.set_window_handle(self.win_id)
+
+ def OnStream(self, widget):
+ print "stream"
+ self.pipeline.set_state(Gst.State.PLAYING)
+
+ def OnStop(self, widget):
+ print "stop"
+ self.pipeline.set_state(Gst.State.READY)
+
+ def OnQuit(self, widget):
+ print "quit"
+ Gtk.main_quit()
+
+ # Workaround to get Ctrl+C to terminate from command line
+ # ref: https://bugzilla.gnome.org/show_bug.cgi?id=622084#c12
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+start = Main()
+Gtk.main()