--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <object class="GtkApplicationWindow" id="mainwindow">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">GStreamer video switcher test</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="viewport_main">
+ <property name="name">viewport_main</property>
+ <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>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="yalign">1</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="button_input1">
+ <property name="label" translatable="yes">Input 1</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_input1_clicked" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="button_input2">
+ <property name="label" translatable="yes">Input 2</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_input2_clicked" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="button_input3">
+ <property name="label" translatable="yes">Input 3</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_input3_clicked" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="button_input4">
+ <property name="label" translatable="yes">Input 4</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_input4_clicked" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="button_input5">
+ <property name="label" translatable="yes">Input 5</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_input5_clicked" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="button_input6">
+ <property name="label" translatable="yes">Input 6</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_input6_clicked" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
--- /dev/null
+#!/usr/bin/env python
+# Attempting to dynamically switch between input sources
+
+# GdkX11 to get access to xid, GstVideo to get access to set_window_handle
+import gi, signal
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gst', '1.0')
+gi.require_version('GstVideo', '1.0')
+from gi.repository import Gtk, Gst, GdkX11, GstVideo
+
+class Main:
+ def __init__(self):
+
+ # Create gui bits and bobs
+
+ self.mainwindow = Gtk.Builder()
+ self.mainwindow.add_from_file("example5.glade")
+
+ signals = {
+ "on_input1_clicked" : self.SetInput1,
+ "on_input2_clicked" : self.SetInput2,
+ "on_input3_clicked" : self.SetInput3,
+ "on_input4_clicked" : self.SetInput4,
+ "on_input5_clicked" : self.SetInput5,
+ "on_input6_clicked" : self.SetInput6,
+ "on_quit_clicked" : self.OnQuit,
+ }
+
+ self.mainwindow.connect_signals(signals)
+
+ # Create GStreamer bits and bobs
+
+ # Initiate Gstreamer
+ Gst.init(None)
+
+ # Some capabilities we want to share between different elements
+ jpegcaps = Gst.Caps.from_string("image/jpeg,width=1280,height=720,framerate=30/1")
+
+ # Set up the pipeline
+ self.pipeline = Gst.Pipeline()
+
+ # Add elements
+
+ # src1
+ self.src1 = Gst.ElementFactory.make("v4l2src", "src1")
+ self.src1.set_property("device", "/dev/video1")
+
+ # src1 capsfilter
+ self.src1caps = Gst.ElementFactory.make("capsfilter", "src1caps")
+ self.src1caps.set_property("caps", jpegcaps)
+
+ # src1jpegdec
+ self.src1jpegdec = Gst.ElementFactory.make("jpegdec", "src1jpegdec")
+
+ # src1videoconvert
+ self.src1videoconvert = Gst.ElementFactory.make("videoconvert", "src1videoconvert")
+
+ # src2
+ self.src2 = Gst.ElementFactory.make("v4l2src", "src2")
+ self.src2.set_property("device", "/dev/video0")
+
+ # src2 capsfilter
+ self.src2caps = Gst.ElementFactory.make("capsfilter", "src2caps")
+ self.src2caps.set_property("caps", jpegcaps)
+
+ # src2jpegdec
+ self.src2jpegdec = Gst.ElementFactory.make("jpegdec", "src2jpegdec")
+
+ # src2videoconvert
+ self.src2videoconvert = Gst.ElementFactory.make("videoconvert", "src2videoconvert")
+
+ # Video mix
+ self.videomix = Gst.ElementFactory.make("videomixer", "videomix")
+ self.src1pad = self.videomix.get_request_pad("sink_1")
+ self.src1pad.set_property("zorder", 2)
+ self.src2pad = self.videomix.get_request_pad("sink_2")
+ self.src2pad.set_property("zorder", 1)
+
+ # Add main videosink
+ self.videosink = Gst.ElementFactory.make("autovideosink", "videosink")
+ self.videosink.set_property("sync", False)
+
+ # Add initial elements to the pipeline
+ self.pipeline.add(self.src1)
+ self.pipeline.add(self.src1caps)
+ self.pipeline.add(self.src1jpegdec)
+ self.pipeline.add(self.src1videoconvert)
+
+ self.pipeline.add(self.src2)
+ self.pipeline.add(self.src2caps)
+ self.pipeline.add(self.src2jpegdec)
+ self.pipeline.add(self.src2videoconvert)
+
+ self.pipeline.add(self.videomix)
+ self.pipeline.add(self.videosink)
+
+ # Link the initial pipeline
+ self.src1.link(self.src1caps)
+ self.src1caps.link(self.src1jpegdec)
+ self.src1jpegdec.link(self.src1videoconvert)
+ self.src1videoconvert.link(self.videomix)
+
+ self.src2.link(self.src2caps)
+ self.src2caps.link(self.src2jpegdec)
+ self.src2jpegdec.link(self.src2videoconvert)
+ self.src2videoconvert.link(self.videomix)
+
+ self.videomix.link(self.videosink)
+
+ # 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_main").get_window().get_xid()
+
+ # Play!
+ self.pipeline.set_state(Gst.State.PLAYING)
+
+
+ # 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 SetInput1(self, widget):
+ print("Switch to input 1")
+ self.src1pad.set_property("zorder", 2)
+ self.src2pad.set_property("zorder", 1)
+
+ def SetInput2(self, widget):
+ print("Switch to input 2")
+ self.src1pad.set_property("zorder", 1)
+ self.src2pad.set_property("zorder", 2)
+
+ def SetInput3(self, widget):
+ print("Switch to input 3")
+
+ def SetInput4(self, widget):
+ print("Switch to input 4")
+
+ def SetInput5(self, widget):
+ print("Switch to input 5")
+
+ def SetInput6(self, widget):
+ print("Switch to input 6")
+
+ 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()