2 # Attempting to dynamically switch between input sources
4 # GdkX11 to get access to xid, GstVideo to get access to set_window_handle
5 import gi
, signal
, time
6 gi
.require_version('Gtk', '3.0')
7 gi
.require_version('Gst', '1.0')
8 gi
.require_version('GstVideo', '1.0')
9 from gi
.repository
import Gtk
, Gst
, GdkX11
, GstVideo
11 stream1
="rtmp://matuku.hig.no/live/planet.stream"
16 # Create gui bits and bobs
18 self
.mainwindow
= Gtk
.Builder()
19 self
.mainwindow
.add_from_file("example5.glade")
22 "on_input1_clicked" : self
.SetInput1
,
23 "on_input2_clicked" : self
.SetInput2
,
24 "on_input3_clicked" : self
.SetInput3
,
25 "on_input4_clicked" : self
.SetInput4
,
26 "on_input5_clicked" : self
.SetInput5
,
27 "on_input6_clicked" : self
.SetInput6
,
28 "on_quit_clicked" : self
.OnQuit
,
31 self
.mainwindow
.connect_signals(signals
)
33 # Create GStreamer bits and bobs
38 # Some capabilities we want to share between different elements
39 jpegcaps
= Gst
.Caps
.from_string("image/jpeg,width=1280,height=720,framerate=30/1")
40 xrawcaps
= Gst
.Caps
.from_string("video/x-raw,width=1280,height=720,framerate=30/1")
43 self
.pipeline
= Gst
.Pipeline()
48 self
.src1
= Gst
.ElementFactory
.make("v4l2src", "src1")
49 self
.src1
.set_property("device", "/dev/video0")
50 self
.src1caps
= Gst
.ElementFactory
.make("capsfilter", "src1caps")
51 self
.src1caps
.set_property("caps", jpegcaps
)
52 self
.src1dec
= Gst
.ElementFactory
.make("decodebin", "src1dec")
55 self
.src2
= Gst
.ElementFactory
.make("videotestsrc", "src2")
56 self
.src2
.set_property("pattern", "ball")
57 self
.src2caps
= Gst
.ElementFactory
.make("capsfilter", "src2caps")
58 self
.src2caps
.set_property("caps", xrawcaps
)
59 self
.src2dec
= Gst
.ElementFactory
.make("decodebin", "src2dec")
62 self
.src3
= Gst
.ElementFactory
.make("rtmpsrc", "src3")
63 self
.src3
.set_property("location", stream1
)
64 self
.src3caps
= Gst
.ElementFactory
.make("capsfilter", "src3caps")
65 self
.src3caps
.set_property("caps", xrawcaps
)
66 self
.src3dec
= Gst
.ElementFactory
.make("decodebin", "src3dec")
67 self
.src3crop
= Gst
.ElementFactory
.make("aspectratiocrop", "src3crop")
68 self
.src3crop
.set_property("aspect-ratio", Gst
.Fraction(16, 9))
69 self
.src3crop
.set_property("message-forward", True)
70 self
.src3croppad
= self
.src3crop
.get_static_pad("sink")
71 self
.src3scale
= Gst
.ElementFactory
.make("videoscale")
72 self
.src3scalecaps
= Gst
.ElementFactory
.make("capsfilter", "src3scalecaps")
73 self
.src3scalecaps
.set_property("caps", Gst
.Caps
.from_string("width=1280,height=720"))
76 self
.videomix
= Gst
.ElementFactory
.make("videomixer", "videomix")
77 self
.src1pad
= self
.videomix
.get_request_pad("sink_1")
78 self
.src1pad
.set_property("zorder", 3)
79 self
.src2pad
= self
.videomix
.get_request_pad("sink_2")
80 self
.src2pad
.set_property("zorder", 2)
81 self
.src3pad
= self
.videomix
.get_request_pad("sink_3")
82 self
.src3pad
.set_property("zorder", 1)
85 self
.videosink
= Gst
.ElementFactory
.make("xvimagesink", "videosink")
86 self
.videosink
.set_property("sync", False)
88 # Add initial elements to the pipeline
89 self
.pipeline
.add(self
.src1
)
90 self
.pipeline
.add(self
.src1caps
)
91 self
.pipeline
.add(self
.src1dec
)
92 self
.src1dec
.connect("pad-added", self
.OnPadAdded
, self
.src1pad
, self
.videomix
)
94 self
.pipeline
.add(self
.src2
)
95 self
.pipeline
.add(self
.src2caps
)
96 self
.pipeline
.add(self
.src2dec
)
97 self
.src2dec
.connect("pad-added", self
.OnPadAdded
, self
.src2pad
, self
.videomix
)
99 self
.pipeline
.add(self
.src3
)
100 self
.pipeline
.add(self
.src3dec
)
101 self
.src3dec
.connect("pad-added", self
.OnPadAdded
, self
.src3croppad
, self
.src3crop
)
102 self
.pipeline
.add(self
.src3crop
)
103 self
.pipeline
.add(self
.src3scale
)
104 self
.pipeline
.add(self
.src3scalecaps
)
106 self
.pipeline
.add(self
.videomix
)
107 self
.pipeline
.add(self
.videosink
)
109 # Link the initial pipeline
110 if not self
.src1
.link(self
.src1caps
):
111 print("Failed to link src1 to src1caps")
112 if not self
.src1caps
.link(self
.src1dec
):
113 print("Failed to link src1caps to src1dec")
114 if not self
.videomix
.link(self
.videosink
):
115 print("Failed to link videomix to videosink")
117 if not self
.src2
.link(self
.src2caps
):
118 print("Failed to link src2 to src2caps")
119 if not self
.src2caps
.link(self
.src2dec
):
120 print("Failed to link src2caps to src2dec")
122 if not self
.src3
.link(self
.src3dec
):
123 print("Failed to link src3 to src3dec")
124 if not self
.src3crop
.link(self
.src3scale
):
125 print("Failed to link src3crop to src3scale")
126 if not self
.src3scale
.link(self
.src3scalecaps
):
127 print("Failed to link src3scale to src3scalecaps")
128 if not self
.src3scalecaps
.link_pads("src", self
.videomix
, "sink_3"):
129 print("Failed to link src3scalecaps to videomix")
131 # Set up a bus to our pipeline to get notified when the video is ready
132 self
.bus
= self
.pipeline
.get_bus()
133 self
.bus
.enable_sync_message_emission()
134 self
.bus
.connect("sync-message::element", self
.OnSyncElement
)
136 # Summon the window and connect the window's close button to quit
137 self
.window
= self
.mainwindow
.get_object("mainwindow")
138 self
.window
.connect("delete-event", Gtk
.main_quit
)
139 self
.window
.show_all()
141 # Get window ID of the viewport widget from the GUI
142 self
.win_id
= self
.mainwindow
.get_object("viewport_main").get_window().get_xid()
145 print(self
.pipeline
.set_state(Gst
.State
.READY
))
146 print(self
.pipeline
.set_state(Gst
.State
.PLAYING
))
147 self
.pipeline
.get_state
150 # When we get a message that video is ready to display, set the
151 # correct window id to hook it to our viewport
152 def OnSyncElement(self
, bus
, message
):
153 if message
.get_structure().get_name() == "prepare-window-handle":
154 print("prepare-window-handle")
155 message
.src
.set_window_handle(self
.win_id
)
157 def OnPadAdded(self
, element
, pad
, sink
, target
):
158 print("Dynamic pad for " + element
.name
+ " added, linking to " + target
.name
)
163 def SetInput1(self
, widget
):
164 print("Switch to input 1")
165 self
.src1pad
.set_property("zorder", 3)
166 self
.src2pad
.set_property("zorder", 2)
167 self
.src3pad
.set_property("zorder", 1)
169 def SetInput2(self
, widget
):
170 print("Switch to input 2")
171 self
.src1pad
.set_property("zorder", 2)
172 self
.src2pad
.set_property("zorder", 3)
173 self
.src3pad
.set_property("zorder", 1)
175 def SetInput3(self
, widget
):
176 print("Switch to input 3")
177 self
.src1pad
.set_property("zorder", 1)
178 self
.src2pad
.set_property("zorder", 2)
179 self
.src3pad
.set_property("zorder", 3)
181 def SetInput4(self
, widget
):
182 print("Switch to input 4")
184 def SetInput5(self
, widget
):
185 print("Switch to input 5")
187 def SetInput6(self
, widget
):
188 print("Switch to input 6")
190 def OnQuit(self
, widget
):
194 # Workaround to get Ctrl+C to terminate from command line
195 # ref: https://bugzilla.gnome.org/show_bug.cgi?id=622084#c12
196 signal
.signal(signal
.SIGINT
, signal
.SIG_DFL
)