Faster example using videomixer
authorEinar Jørgen Haraldseid <einar@haraldseid.net>
Sat, 3 Jun 2017 20:52:00 +0000 (22:52 +0200)
committerEinar Jørgen Haraldseid <einar@haraldseid.net>
Sat, 3 Jun 2017 20:52:00 +0000 (22:52 +0200)
example5.glade [new file with mode: 0644]
example5.py [new file with mode: 0755]

diff --git a/example5.glade b/example5.glade
new file mode 100644 (file)
index 0000000..9d3d36d
--- /dev/null
@@ -0,0 +1,177 @@
+<?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>
diff --git a/example5.py b/example5.py
new file mode 100755 (executable)
index 0000000..44ace18
--- /dev/null
@@ -0,0 +1,165 @@
+#!/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()