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