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