Automatic call of backup pipeline when feed is lost.
[libre-streamer.git] / stream_2016 / gstconf.py
CommitLineData
669383aa
DT
1#!/usr/bin/env python3.4
2
3# This file is part of Libre-Streamer.
4#
5# Libre-Streamer is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Libre-Streamer is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Libre-Streamer. If not, see <http://www.gnu.org/licenses/>.
17#
18# Copyright (c) 2016 David Testé
19
340ab727 20from os import rename
e84c1bd7 21from os import listdir
340ab727 22
669383aa
DT
23import gi
24from gi.repository import Gst
25from gi.repository import GstVideo
26
340ab727
DT
27# Pathname has to be defined
28PATHNAME = ''
29AUDIO_DEFAULT = PATHNAME + 'AUDIO_DEFAULT'
30RAWVIDEO_DEFAULT = PATHNAME + 'RAWVIDEO_DEFAULT'
31STREAM_DEFAULT = PATHNAME + 'STREAM_DEFAULT'
32
33
669383aa 34class New_user_pipeline():
340ab727 35
669383aa 36
e84c1bd7
DT
37 def __init__(self, feed='main'):
38 self.feed = feed
39 if self.feed == 'main':
40 self.user_pipeline = self.create_gstreamer_pipeline()
41 elif self.feed == 'backup':
42 self.user_pipeline = self.create_gstreamer_pipeline(feed='backup')
43
669383aa
DT
44 def create_video_sources(self):
45 """Create video inputs from various sources."""
46 self.videosrc = Gst.ElementFactory.make('rtspsrc', 'videosrc')
47 self.videosrc.set_property('location', 'rtsp://192.168.48.2:554')
48 self.videosrc.set_property('latency', 0)
e84c1bd7
DT
49 if self.feed == 'backup':
50 self.videosrc_backup = Gst.ElementFactory.make('v4l2src',
51 'videosrc_backup')
52 device_location = self.find_webcam_device()
53 print ('[INFO] Webcam device location: ', device_location)
54 self.videosrc_backup.set_property('device', device_location)
55
56 def find_webcam_device(self):
57 """Look out for the USB webcam device."""
58 devices = [dev for dev in listdir('/dev/') if 'video' in dev]
59 for item in devices:
60 # In case of computer having a built-in webcam
61 if item != 'video0' and len(devices) > 1:
62 return '/dev/' + item
63 # Without built-in webcam
64 elif len(devices) == 1:
65 return '/dev/video0'
66 print('[ERROR] No webcam device found.')
669383aa 67
669383aa
DT
68 def create_pipeline_callbacks(self):
69 """Callbacks to connect dynamically created pads."""
e84c1bd7 70 self.videosrc.connect('pad-added', self.on_pad_added_to_rtspsrc)
669383aa
DT
71
72 def on_pad_added_to_rtspsrc(self, rtspsrc, pad):
73 """Connect the dynamic 'src'pad of an RTSP source."""
74 sinkpad = self.queuev_1.get_static_pad('sink')
75 pad.link(sinkpad)
76
e84c1bd7
DT
77 def create_audio_sources(self):
78 """Create audio inputs from various sources."""
79 self.audiosrc = Gst.ElementFactory.make('pulsesrc', 'audiosrc')
80## self.videosrc.set_property('latency', 0)
81
6db3115f
DT
82 def create_audiolevel_plugin(self):
83 """Create audio level plugin to feed a vu-meter."""
84 self.audiolevel = Gst.ElementFactory.make('level', 'audiolevel')
85 self.audiolevel.set_property('interval', 200000000)
86
669383aa
DT
87 def create_filesink(self):
88 """Create storable output elements."""
89 self.disksink_rawvideo = Gst.ElementFactory.make('filesink')
90 #[TO DO]: File location has to be defined
340ab727 91 self.disksink_rawvideo.set_property('location', RAWVIDEO_DEFAULT)
669383aa 92 self.disksink_audio = Gst.ElementFactory.make('filesink')
340ab727 93 self.disksink_audio.set_property('location', AUDIO_DEFAULT)
669383aa 94 self.disksink_stream = Gst.ElementFactory.make('filesink')
340ab727 95 self.disksink_stream.set_property('location', STREAM_DEFAULT)
669383aa
DT
96
97 def create_streamsink(self):
98 """Create streamable output elements."""
99 # To local screen:
100 self.screensink = Gst.ElementFactory.make('xvimagesink', 'screensink')
e84c1bd7 101 self.screensink.set_property('sync', False)
669383aa
DT
102 # To icecast server:
103 self.icecastsink_audio = Gst.ElementFactory.make('shout2send', 'icecastsink_audio')
104## Configuration should be written on a file locally to keep safe private addresses
105 self.icecastsink_audio.set_property('ip', 'live2.fsf.org')
106 self.icecastsink_audio.set_property('port', 80)
107 self.icecastsink_audio.set_property('mount', 'testaudio.ogv')
108 self.icecastsink_audio.set_property('password', 'thahw3Wiez')
109 self.icecastsink_stream = Gst.ElementFactory.make('shout2send', 'icecastsink_stream')
110 self.icecastsink_stream.set_property('ip', 'live2.fsf.org')
111 self.icecastsink_stream.set_property('port', 80)
112 self.icecastsink_stream.set_property('mount', 'teststream.ogv')
113 self.icecastsink_stream.set_property('password', 'thahw3Wiez')
114
115 def create_payloader_elements(self):
116 pass
117
118 def create_depayloader_elements(self):
119 self.rtpjpegdepay = Gst.ElementFactory.make('rtpjpegdepay', 'rtpjpegdepay')
120
121 def create_encoder_elements(self):
122 # Audio encoders:
123 self.vorbisenc = Gst.ElementFactory.make('vorbisenc', 'vorbisenc')
124 # Video encoders:
125 self.vp8enc = Gst.ElementFactory.make('vp8enc', 'vp8enc')
126 self.vp8enc.set_property('min_quantizer', 1)
127 self.vp8enc.set_property('max_quantizer', 13)
128 self.vp8enc.set_property('cpu-used', 5)
129 self.vp8enc.set_property('deadline', 42000)
130 self.vp8enc.set_property('threads', 2)
131 self.vp8enc.set_property('sharpness', 7)
132
133 def create_decoder_elements(self):
134 self.jpegdec = Gst.ElementFactory.make('jpegdec', 'jpegdec')
135 self.jpegdec.set_property('max-errors', -1)
136
137 def create_muxer_elements(self):
138 self.oggmux = Gst.ElementFactory.make('oggmux', 'oggmux')
139 self.mkvmux = Gst.ElementFactory.make('matroskamux', 'mkvmux')
140 self.webmmux = Gst.ElementFactory.make('webmmux', 'webmmux')
141 self.webmmux.set_property('streamable', True)
142
143 def create_demuxer_elements(self):
144 pass
145
146 def create_filtering_elements(self):
147 self.scaling = Gst.ElementFactory.make('videoscale', 'scaling')
148 caps = Gst.caps_from_string('video/x-raw, width=(int)640, height=(int)360')
149 self.capsfilter = Gst.ElementFactory.make('capsfilter', 'capsfilter')
150 self.capsfilter.set_property('caps', caps)
e84c1bd7
DT
151
152 caps_backup = Gst.caps_from_string('video/x-raw, width=(int)640, height=(int)360')
153 self.capsfilter_backup = Gst.ElementFactory.make('capsfilter', 'capsfilter_backup')
154 self.capsfilter_backup.set_property('caps', caps_backup)
669383aa
DT
155
156 def create_tee_elements(self):
157 """Create tee elements to divide feeds."""
158 self.tee_rawvideo = Gst.ElementFactory.make('tee', 'tee_rawvideo')
159 self.tee_videodecoded = Gst.ElementFactory.make('tee', 'tee_videodecoded')
160 self.tee_streamfull = Gst.ElementFactory.make('tee', 'tee_streamfull')
161 self.tee_rawaudio = Gst.ElementFactory.make('tee', 'tee_rawaudio')
162 self.tee_streamaudio = Gst.ElementFactory.make('tee', 'tee_streamaudio')
163
164 def connect_tee(self,
165 tee_element,
166 input_element,
167 output_element_1,
168 output_element_2,):
169 """Links input and outputs of a given tee element."""
170 # Find a way to check if the element given are in the pipeline
171 # then pass the result to the 'if' statement.
172 ## argcheck = [True for arg in locals() if arg in 'the_list_of_elements_added']
173 ## print('[DEBUG] ArgList check: ', argcheck)
174 ## if False not in argcheck
175 if True:
176 input_element.link(tee_element)
177 tee_element.link(output_element_1)
178 tee_element.link(output_element_2)
179 else:
180 print('[ERROR] Couldn\'t link the tee. Element(s) probably not in the pipeline ')
181
182 def create_queues(self):
183 # For video feed:
184 self.queuev_1 = Gst.ElementFactory.make('queue', 'queuev_1')
185 self.queuev_2 = Gst.ElementFactory.make('queue', 'queuev_2')
186 self.queuev_3 = Gst.ElementFactory.make('queue', 'queuev_3')
187 self.queuev_4 = Gst.ElementFactory.make('queue', 'queuev_4')
188 self.queuev_5 = Gst.ElementFactory.make('queue', 'queuev_5')
189 self.queuev_6 = Gst.ElementFactory.make('queue', 'queuev_6')
190 # For audio feed:
191 self.queuea_1 = Gst.ElementFactory.make('queue', 'queuea_1')
192 self.queuea_2 = Gst.ElementFactory.make('queue', 'queuea_2')
193 self.queuea_3 = Gst.ElementFactory.make('queue', 'queuea_3')
194 self.queuea_4 = Gst.ElementFactory.make('queue', 'queuea_4')
195 self.queuea_5 = Gst.ElementFactory.make('queue', 'queuea_5')
196 # For audio+video muxer:
197 self.queuem_1 = Gst.ElementFactory.make('queue', 'queuem_1')
198 self.queuem_2 = Gst.ElementFactory.make('queue', 'queuem_2')
199
200 def create_pipeline_elements(self):
201 print('Pipeline creation state: creating elements... ', end='')
202 # Inputs elements:
203 self.create_video_sources()
204 self.create_audio_sources()
205 # Middle elements:
6db3115f 206 self.create_audiolevel_plugin()
669383aa
DT
207 self.create_payloader_elements()
208 self.create_depayloader_elements()
209 self.create_encoder_elements()
210 self.create_decoder_elements()
211 self.create_muxer_elements()
212 self.create_filtering_elements()
213 self.create_tee_elements()
214 self.create_queues()
215 # Output elements:
216 self.create_filesink()
217 self.create_streamsink()
218 print('created')
219
220
6db3115f
DT
221 def add_elements_to_pipeline(self, feed='main'):
222 print('Pipeline creation state: adding elements... ', end='')
223
224 if feed == 'main':
6db3115f 225 # Add here the elments associated with the RTSP feed
e84c1bd7
DT
226 # Inputs elements:
227 self.streampipe.add(self.videosrc)
228 self.streampipe.add(self.audiosrc)
229 # Middle elements:
230 self.streampipe.add(self.audiolevel)
231 self.streampipe.add(self.rtpjpegdepay)
232 self.streampipe.add(self.jpegdec)
233 self.streampipe.add(self.tee_rawvideo)
234 self.streampipe.add(self.mkvmux)
235 self.streampipe.add(self.vorbisenc)
236 self.streampipe.add(self.oggmux)
237 self.streampipe.add(self.scaling)
238 self.streampipe.add(self.capsfilter)
239 self.streampipe.add(self.vp8enc)
240 self.streampipe.add(self.webmmux)
241 self.streampipe.add(self.tee_rawaudio)
242 self.streampipe.add(self.tee_streamaudio)
243 self.streampipe.add(self.tee_videodecoded)
244 self.streampipe.add(self.tee_streamfull)
245 self.streampipe.add(self.queuev_1)
246 self.streampipe.add(self.queuev_2)
247 self.streampipe.add(self.queuev_3)
248 self.streampipe.add(self.queuev_4)
249 self.streampipe.add(self.queuev_5)
250 self.streampipe.add(self.queuev_6)
251 self.streampipe.add(self.queuea_1)
252 self.streampipe.add(self.queuea_2)
253 self.streampipe.add(self.queuea_3)
254 self.streampipe.add(self.queuea_4)
255 self.streampipe.add(self.queuea_5)
256 self.streampipe.add(self.queuem_1)
257 self.streampipe.add(self.queuem_2)
258 # Outputs elements:
259 self.streampipe.add(self.screensink)
260 self.streampipe.add(self.disksink_rawvideo)
261 self.streampipe.add(self.disksink_audio)
262 self.streampipe.add(self.disksink_stream)
263 self.streampipe.add(self.icecastsink_audio)
264 self.streampipe.add(self.icecastsink_stream)
6db3115f 265 elif feed == 'backup':
6db3115f 266 # Add here the elments associated with the WEBCAM feed
e84c1bd7
DT
267 self.streampipe.add(self.videosrc_backup)
268 self.streampipe.add(self.capsfilter_backup)
269 self.streampipe.add(self.queuev_1)
270 self.streampipe.add(self.screensink)
271 print ('BACKUP OK...', end='')
669383aa
DT
272 print('added')
273
6db3115f 274 def link_pipeline_elements(self, feed='main'):
669383aa
DT
275 """Link all elements with static pads."""
276 print('Pipeline creation state: linking elements... ', end='')
6db3115f
DT
277
278 if feed == 'main':
6db3115f 279 # linking here RTSP feed
e84c1bd7
DT
280 # Video feed:
281 self.queuev_1.link(self.rtpjpegdepay)
282 self.connect_tee(self.tee_rawvideo,
283 self.rtpjpegdepay,
284 self.queuev_2,
285 self.jpegdec,)
286 self.queuev_2.link(self.mkvmux)
287 self.mkvmux.link(self.queuev_4)
288 self.queuev_4.link(self.disksink_rawvideo)
289 self.connect_tee(self.tee_videodecoded,
290 self.jpegdec,
291 self.queuev_3,
292 self.scaling,)
293 self.queuev_3.link(self.screensink)
294 # Audio feed:
295 self.audiosrc.link(self.audiolevel)
296 self.audiolevel.link(self.queuea_1)
297 self.queuea_1.link(self.vorbisenc)
298 self.connect_tee(self.tee_rawaudio,
299 self.vorbisenc,
300 self.queuea_2,
301 self.queuea_5,)
302 self.queuea_2.link(self.oggmux)
303 self.connect_tee(self.tee_streamaudio,
304 self.oggmux,
305 self.queuea_3,
306 self.queuea_4,)
307 self.queuea_3.link(self.disksink_audio)
308 self.queuea_4.link(self.icecastsink_audio)
309 self.queuea_5.link(self.webmmux)
310 # Stream (audio+video) feed:
311 self.scaling.link(self.capsfilter)
312 self.capsfilter.link(self.vp8enc)
313 self.vp8enc.link(self.queuev_6)
314 self.queuev_6.link(self.webmmux)
315 self.connect_tee(self.tee_streamfull,
316 self.webmmux,
317 self.queuem_1,
318 self.queuem_2,)
319 self.queuem_1.link(self.disksink_stream)
320 self.queuem_2.link(self.icecastsink_stream)
6db3115f 321 elif feed == 'backup':
6db3115f 322 # linking here backup feed (WEBCAM)
e84c1bd7
DT
323 self.videosrc_backup.link(self.capsfilter_backup)
324 self.capsfilter_backup.link(self.queuev_1)
325 self.queuev_1.link(self.screensink)
326 print('BACKUP OK...', end='')
669383aa
DT
327 print('linked')
328
e84c1bd7 329 def create_gstreamer_pipeline(self, feed='main'):
669383aa
DT
330 # New empty pipeline:
331 self.streampipe = Gst.Pipeline()
669383aa 332 self.create_pipeline_elements()
e84c1bd7
DT
333 # Setting-up:
334 if feed == 'main':
335 self.add_elements_to_pipeline()
336 self.link_pipeline_elements()
337 self.create_pipeline_callbacks()
338 elif feed == 'backup':
339 self.add_elements_to_pipeline(feed='backup')
340 self.link_pipeline_elements(feed='backup')
669383aa
DT
341
342 global bus
343 bus = self.streampipe.get_bus()
344 bus.add_signal_watch()
345 bus.enable_sync_message_emission()
346 # Used to get messages that GStreamer emits.
347 bus.connect("message", self.on_message)
348
349 print('Pipeline creation state: successfully done.')
350 return self.streampipe
6db3115f
DT
351
352 def create_gstreamer_backup_pipeline(self):
353 """Creates a backup pipeline based on a webcam feed."""
354 print("CREATE HERE A BACKUP PIPELINE")
669383aa
DT
355
356 def on_message(self, bus, message):
e84c1bd7
DT
357 #
358## print("[MESSAGE]", message.get_structure().get_name()) # [DEBUG]
359 #
669383aa
DT
360 t = message.type
361 if t == Gst.MessageType.EOS:
6db3115f 362 self.streampipe.set_state(Gst.State.NULL)
669383aa
DT
363 elif t == Gst.MessageType.ERROR:
364 err, debug = message.parse_error()
365 print ("Error: %s" % err, debug)
6db3115f 366 self.streampipe.set_state(Gst.State.NULL)
e84c1bd7
DT
367## self.create_gstreamer_backup_pipeline()
368## return 'ERROR'
369
669383aa
DT
370 def stream_play(self):
371 self.streampipe.set_state(Gst.State.PLAYING)
e84c1bd7 372 print('[INFO] PLAYING State resquested')
669383aa
DT
373
374 def stream_stop(self):
375 self.streampipe.set_state(Gst.State.NULL)
376
377 def get_stream_state(self):
378 print(self.streampipe.get_state(self))
da450d89 379##[FIXME] return self.streampipe.get_state()
340ab727
DT
380
381 def set_filenames(self, string):
382 """Sets filename and location for each sink."""
383 filename = string
384 audio = PATHNAME + filename + '_AUDIO'
385 rawvideo = PATHNAME + filename + '_RAWVIDEO'
386 stream = PATHNAME + filename + '_STREAM'
387 rename(AUDIO_DEFAULT, audio)
388 rename(RAWVIDEO_DEFAULT, rawvideo)
389 rename(STREAM_DEFAULT, stream)
669383aa
DT
390
391def get_gstreamer_bus():
392 return bus
340ab727 393