Example switching between two video sources (but very slow)
authorEinar Jørgen Haraldseid <einar@haraldseid.net>
Sat, 3 Jun 2017 16:58:15 +0000 (18:58 +0200)
committerEinar Jørgen Haraldseid <einar@haraldseid.net>
Sat, 3 Jun 2017 16:58:15 +0000 (18:58 +0200)
example4.glade [new file with mode: 0644]
example4.py [new file with mode: 0755]

diff --git a/example4.glade b/example4.glade
new file mode 100644 (file)
index 0000000..6d7a61a
--- /dev/null
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-orientation-landscape</property>
+    <property name="icon_size">3</property>
+  </object>
+  <object class="GtkImage" id="image2">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-orientation-reverse-landscape</property>
+    <property name="icon_size">3</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>
+            <property name="margin_left">8</property>
+            <property name="margin_right">8</property>
+            <property name="margin_top">8</property>
+            <child>
+              <object class="GtkDrawingArea" id="viewport">
+                <property name="name">viewport</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="alignment1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_left">8</property>
+            <property name="margin_right">8</property>
+            <property name="margin_top">8</property>
+            <property name="margin_bottom">8</property>
+            <property name="yalign">1</property>
+            <child>
+              <object class="GtkHButtonBox" id="buttonbox1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">8</property>
+                <property name="homogeneous">True</property>
+                <property name="baseline_position">bottom</property>
+                <property name="layout_style">center</property>
+                <child>
+                  <object class="GtkButton" id="input1">
+                    <property name="label">Input 1</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="image">image1</property>
+                    <property name="always_show_image">True</property>
+                    <signal name="clicked" handler="on_input1_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="input2">
+                    <property name="label">Input 2</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="image">image2</property>
+                    <property name="always_show_image">True</property>
+                    <signal name="clicked" handler="on_input2_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</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>
+                    <property name="always_show_image">True</property>
+                    <signal name="clicked" handler="on_quit_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</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="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/example4.py b/example4.py
new file mode 100755 (executable)
index 0000000..7398782
--- /dev/null
@@ -0,0 +1,129 @@
+#!/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("example4.glade")
+
+        signals = {
+            "on_input1_clicked" : self.SetInput1,
+            "on_input2_clicked" : self.SetInput2,
+            "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
+        commoncaps = Gst.Caps.from_string("video/x-raw,width=1280,height=720,framerate=30/1")
+
+        # Set up the pipeline
+        self.pipeline = Gst.Pipeline()
+
+        # Add a videotestsrc element, set it to pattern "ball" and some other properties
+        self.ball = Gst.ElementFactory.make("videotestsrc", "ball")
+        self.ball.set_property("pattern", "ball")
+        self.ball.set_property("flip", True)
+
+        # Add a capsfilter that we want to apply to our ball
+        self.ballcaps = Gst.ElementFactory.make("capsfilter", "ballcaps")
+        self.ballcaps.set_property("caps", commoncaps)
+
+        # Webcam source
+        self.webcam = Gst.ElementFactory.make("v4l2src", "webcam")
+        self.webcam.set_property("device", "/dev/video0")
+
+        # Webcam capsfilter
+        self.webcamcaps = Gst.ElementFactory.make("capsfilter", "webcamcaps")
+        self.webcamcaps.set_property("caps", commoncaps)
+
+        # Add a videosink (where the playback happens)
+        self.videosink = Gst.ElementFactory.make("autovideosink", "videosink")
+        self.videosink.set_property("sync", False)
+
+        # Add initial elements to the pipeline
+        self.pipeline.add(self.ball)
+        self.pipeline.add(self.ballcaps)
+        self.pipeline.add(self.videosink)
+
+        # Link the initial pipeline
+        self.ball.link(self.ballcaps)
+        self.ballcaps.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").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")
+        # Halt current pipeline and tear it down
+        self.pipeline.set_state(Gst.State.NULL)
+        self.webcamcaps.unlink(self.videosink)
+        self.pipeline.remove(self.webcam)
+        self.pipeline.remove(self.webcamcaps)
+        # Construct new pipeline
+        self.pipeline.add(self.ball)
+        self.pipeline.add(self.ballcaps)
+        self.ball.link(self.ballcaps)
+        self.ballcaps.link(self.videosink)
+        self.pipeline.set_state(Gst.State.PLAYING)
+
+    def SetInput2(self, widget):
+        print("Switch to input 2")
+        # Halt current pipeline and tear it down
+        self.pipeline.set_state(Gst.State.NULL)
+        self.ballcaps.unlink(self.videosink)
+        self.pipeline.remove(self.ball)
+        self.pipeline.remove(self.ballcaps)
+        # Construct new pipeline
+        self.pipeline.add(self.webcam)
+        self.pipeline.add(self.webcamcaps)
+        self.webcam.link(self.webcamcaps)
+        self.webcamcaps.link(self.videosink)
+        self.pipeline.set_state(Gst.State.PLAYING)
+
+    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()