Starting implementation of the backup feed (USB webcam based)
[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
DT
20from os import rename
21
669383aa
DT
22import gi
23from gi.repository import Gst
24from gi.repository import GstVideo
25
340ab727
DT
26# Pathname has to be defined
27PATHNAME = ''
28AUDIO_DEFAULT = PATHNAME + 'AUDIO_DEFAULT'
29RAWVIDEO_DEFAULT = PATHNAME + 'RAWVIDEO_DEFAULT'
30STREAM_DEFAULT = PATHNAME + 'STREAM_DEFAULT'
6db3115f 31WEBCAMUSB_DEFAULT = '/dev/video1'
340ab727
DT
32
33
669383aa 34class New_user_pipeline():
340ab727 35
669383aa
DT
36
37 def __init__(self):
38 self.user_pipeline = self.create_gstreamer_pipeline()
39
40 def create_video_sources(self):
41 """Create video inputs from various sources."""
42 self.videosrc = Gst.ElementFactory.make('rtspsrc', 'videosrc')
43 self.videosrc.set_property('location', 'rtsp://192.168.48.2:554')
44 self.videosrc.set_property('latency', 0)
6db3115f
DT
45## self.videosrc = Gst.ElementFactory.make('v4l2src', 'videosrc')
46## self.videosrc.set_property('device', '/dev/video1')
669383aa
DT
47
48 def create_audio_sources(self):
49 """Create audio inputs from various sources."""
50 self.audiosrc = Gst.ElementFactory.make('pulsesrc', 'audiosrc')
51## self.videosrc.set_property('latency', 0)
52
53 def create_pipeline_callbacks(self):
54 """Callbacks to connect dynamically created pads."""
55 self.videosrc.connect("pad-added", self.on_pad_added_to_rtspsrc)
56
57 def on_pad_added_to_rtspsrc(self, rtspsrc, pad):
58 """Connect the dynamic 'src'pad of an RTSP source."""
59 sinkpad = self.queuev_1.get_static_pad('sink')
60 pad.link(sinkpad)
61
6db3115f
DT
62 def create_audiolevel_plugin(self):
63 """Create audio level plugin to feed a vu-meter."""
64 self.audiolevel = Gst.ElementFactory.make('level', 'audiolevel')
65 self.audiolevel.set_property('interval', 200000000)
66
669383aa
DT
67 def create_filesink(self):
68 """Create storable output elements."""
69 self.disksink_rawvideo = Gst.ElementFactory.make('filesink')
70 #[TO DO]: File location has to be defined
340ab727 71 self.disksink_rawvideo.set_property('location', RAWVIDEO_DEFAULT)
669383aa 72 self.disksink_audio = Gst.ElementFactory.make('filesink')
340ab727 73 self.disksink_audio.set_property('location', AUDIO_DEFAULT)
669383aa 74 self.disksink_stream = Gst.ElementFactory.make('filesink')
340ab727 75 self.disksink_stream.set_property('location', STREAM_DEFAULT)
669383aa
DT
76
77 def create_streamsink(self):
78 """Create streamable output elements."""
79 # To local screen:
80 self.screensink = Gst.ElementFactory.make('xvimagesink', 'screensink')
81 # To icecast server:
82 self.icecastsink_audio = Gst.ElementFactory.make('shout2send', 'icecastsink_audio')
83## Configuration should be written on a file locally to keep safe private addresses
84 self.icecastsink_audio.set_property('ip', 'live2.fsf.org')
85 self.icecastsink_audio.set_property('port', 80)
86 self.icecastsink_audio.set_property('mount', 'testaudio.ogv')
87 self.icecastsink_audio.set_property('password', 'thahw3Wiez')
88 self.icecastsink_stream = Gst.ElementFactory.make('shout2send', 'icecastsink_stream')
89 self.icecastsink_stream.set_property('ip', 'live2.fsf.org')
90 self.icecastsink_stream.set_property('port', 80)
91 self.icecastsink_stream.set_property('mount', 'teststream.ogv')
92 self.icecastsink_stream.set_property('password', 'thahw3Wiez')
93
94 def create_payloader_elements(self):
95 pass
96
97 def create_depayloader_elements(self):
98 self.rtpjpegdepay = Gst.ElementFactory.make('rtpjpegdepay', 'rtpjpegdepay')
99
100 def create_encoder_elements(self):
101 # Audio encoders:
102 self.vorbisenc = Gst.ElementFactory.make('vorbisenc', 'vorbisenc')
103 # Video encoders:
104 self.vp8enc = Gst.ElementFactory.make('vp8enc', 'vp8enc')
105 self.vp8enc.set_property('min_quantizer', 1)
106 self.vp8enc.set_property('max_quantizer', 13)
107 self.vp8enc.set_property('cpu-used', 5)
108 self.vp8enc.set_property('deadline', 42000)
109 self.vp8enc.set_property('threads', 2)
110 self.vp8enc.set_property('sharpness', 7)
111
112 def create_decoder_elements(self):
113 self.jpegdec = Gst.ElementFactory.make('jpegdec', 'jpegdec')
114 self.jpegdec.set_property('max-errors', -1)
115
116 def create_muxer_elements(self):
117 self.oggmux = Gst.ElementFactory.make('oggmux', 'oggmux')
118 self.mkvmux = Gst.ElementFactory.make('matroskamux', 'mkvmux')
119 self.webmmux = Gst.ElementFactory.make('webmmux', 'webmmux')
120 self.webmmux.set_property('streamable', True)
121
122 def create_demuxer_elements(self):
123 pass
124
125 def create_filtering_elements(self):
126 self.scaling = Gst.ElementFactory.make('videoscale', 'scaling')
127 caps = Gst.caps_from_string('video/x-raw, width=(int)640, height=(int)360')
128 self.capsfilter = Gst.ElementFactory.make('capsfilter', 'capsfilter')
129 self.capsfilter.set_property('caps', caps)
130
131 def create_tee_elements(self):
132 """Create tee elements to divide feeds."""
133 self.tee_rawvideo = Gst.ElementFactory.make('tee', 'tee_rawvideo')
134 self.tee_videodecoded = Gst.ElementFactory.make('tee', 'tee_videodecoded')
135 self.tee_streamfull = Gst.ElementFactory.make('tee', 'tee_streamfull')
136 self.tee_rawaudio = Gst.ElementFactory.make('tee', 'tee_rawaudio')
137 self.tee_streamaudio = Gst.ElementFactory.make('tee', 'tee_streamaudio')
138
139 def connect_tee(self,
140 tee_element,
141 input_element,
142 output_element_1,
143 output_element_2,):
144 """Links input and outputs of a given tee element."""
145 # Find a way to check if the element given are in the pipeline
146 # then pass the result to the 'if' statement.
147 ## argcheck = [True for arg in locals() if arg in 'the_list_of_elements_added']
148 ## print('[DEBUG] ArgList check: ', argcheck)
149 ## if False not in argcheck
150 if True:
151 input_element.link(tee_element)
152 tee_element.link(output_element_1)
153 tee_element.link(output_element_2)
154 else:
155 print('[ERROR] Couldn\'t link the tee. Element(s) probably not in the pipeline ')
156
157 def create_queues(self):
158 # For video feed:
159 self.queuev_1 = Gst.ElementFactory.make('queue', 'queuev_1')
160 self.queuev_2 = Gst.ElementFactory.make('queue', 'queuev_2')
161 self.queuev_3 = Gst.ElementFactory.make('queue', 'queuev_3')
162 self.queuev_4 = Gst.ElementFactory.make('queue', 'queuev_4')
163 self.queuev_5 = Gst.ElementFactory.make('queue', 'queuev_5')
164 self.queuev_6 = Gst.ElementFactory.make('queue', 'queuev_6')
165 # For audio feed:
166 self.queuea_1 = Gst.ElementFactory.make('queue', 'queuea_1')
167 self.queuea_2 = Gst.ElementFactory.make('queue', 'queuea_2')
168 self.queuea_3 = Gst.ElementFactory.make('queue', 'queuea_3')
169 self.queuea_4 = Gst.ElementFactory.make('queue', 'queuea_4')
170 self.queuea_5 = Gst.ElementFactory.make('queue', 'queuea_5')
171 # For audio+video muxer:
172 self.queuem_1 = Gst.ElementFactory.make('queue', 'queuem_1')
173 self.queuem_2 = Gst.ElementFactory.make('queue', 'queuem_2')
174
175 def create_pipeline_elements(self):
176 print('Pipeline creation state: creating elements... ', end='')
177 # Inputs elements:
178 self.create_video_sources()
179 self.create_audio_sources()
180 # Middle elements:
6db3115f 181 self.create_audiolevel_plugin()
669383aa
DT
182 self.create_payloader_elements()
183 self.create_depayloader_elements()
184 self.create_encoder_elements()
185 self.create_decoder_elements()
186 self.create_muxer_elements()
187 self.create_filtering_elements()
188 self.create_tee_elements()
189 self.create_queues()
190 # Output elements:
191 self.create_filesink()
192 self.create_streamsink()
193 print('created')
194
195
6db3115f
DT
196 def add_elements_to_pipeline(self, feed='main'):
197 print('Pipeline creation state: adding elements... ', end='')
198
199 if feed == 'main':
200 pass
201 # Add here the elments associated with the RTSP feed
202 elif feed == 'backup':
203 pass
204 # Add here the elments associated with the WEBCAM feed
205
669383aa
DT
206 # Inputs elements:
207 self.streampipe.add(self.videosrc)
208 self.streampipe.add(self.audiosrc)
209 # Middle elements:
6db3115f 210 self.streampipe.add(self.audiolevel)
669383aa
DT
211 self.streampipe.add(self.rtpjpegdepay)
212 self.streampipe.add(self.jpegdec)
213 self.streampipe.add(self.tee_rawvideo)
214 self.streampipe.add(self.mkvmux)
215 self.streampipe.add(self.vorbisenc)
216 self.streampipe.add(self.oggmux)
217 self.streampipe.add(self.scaling)
218 self.streampipe.add(self.capsfilter)
219 self.streampipe.add(self.vp8enc)
220 self.streampipe.add(self.webmmux)
221 self.streampipe.add(self.tee_rawaudio)
222 self.streampipe.add(self.tee_streamaudio)
223 self.streampipe.add(self.tee_videodecoded)
224 self.streampipe.add(self.tee_streamfull)
225 self.streampipe.add(self.queuev_1)
226 self.streampipe.add(self.queuev_2)
227 self.streampipe.add(self.queuev_3)
228 self.streampipe.add(self.queuev_4)
229 self.streampipe.add(self.queuev_5)
230 self.streampipe.add(self.queuev_6)
231 self.streampipe.add(self.queuea_1)
232 self.streampipe.add(self.queuea_2)
233 self.streampipe.add(self.queuea_3)
234 self.streampipe.add(self.queuea_4)
235 self.streampipe.add(self.queuea_5)
236 self.streampipe.add(self.queuem_1)
237 self.streampipe.add(self.queuem_2)
238 # Outputs elements:
239 self.streampipe.add(self.screensink)
240 self.streampipe.add(self.disksink_rawvideo)
241 self.streampipe.add(self.disksink_audio)
242 self.streampipe.add(self.disksink_stream)
243 self.streampipe.add(self.icecastsink_audio)
244 self.streampipe.add(self.icecastsink_stream)
245 print('added')
246
6db3115f 247 def link_pipeline_elements(self, feed='main'):
669383aa
DT
248 """Link all elements with static pads."""
249 print('Pipeline creation state: linking elements... ', end='')
6db3115f
DT
250
251 if feed == 'main':
252 pass
253 # linking here RTSP feed
254 elif feed == 'backup':
255 pass
256 # linking here backup feed (WEBCAM)
257
669383aa
DT
258 # Video feed:
259 self.queuev_1.link(self.rtpjpegdepay)
260 self.connect_tee(self.tee_rawvideo,
261 self.rtpjpegdepay,
262 self.queuev_2,
263 self.jpegdec,)
264 self.queuev_2.link(self.mkvmux)
265 self.mkvmux.link(self.queuev_4)
266 self.queuev_4.link(self.disksink_rawvideo)
267 self.connect_tee(self.tee_videodecoded,
268 self.jpegdec,
269 self.queuev_3,
270 self.scaling,)
271 self.queuev_3.link(self.screensink)
272 # Audio feed:
6db3115f
DT
273 self.audiosrc.link(self.audiolevel)
274 self.audiolevel.link(self.queuea_1)
669383aa
DT
275 self.queuea_1.link(self.vorbisenc)
276 self.connect_tee(self.tee_rawaudio,
277 self.vorbisenc,
278 self.queuea_2,
279 self.queuea_5,)
280 self.queuea_2.link(self.oggmux)
281 self.connect_tee(self.tee_streamaudio,
282 self.oggmux,
283 self.queuea_3,
284 self.queuea_4,)
285 self.queuea_3.link(self.disksink_audio)
286 self.queuea_4.link(self.icecastsink_audio)
287 self.queuea_5.link(self.webmmux)
288 # Stream (audio+video) feed:
289 self.scaling.link(self.capsfilter)
290 self.capsfilter.link(self.vp8enc)
291 self.vp8enc.link(self.queuev_6)
292 self.queuev_6.link(self.webmmux)
293 self.connect_tee(self.tee_streamfull,
294 self.webmmux,
295 self.queuem_1,
296 self.queuem_2,)
297 self.queuem_1.link(self.disksink_stream)
298 self.queuem_2.link(self.icecastsink_stream)
299 print('linked')
300
301 def create_gstreamer_pipeline(self):
302 # New empty pipeline:
303 self.streampipe = Gst.Pipeline()
304 # Setting-up:
305 self.create_pipeline_elements()
306 self.add_elements_to_pipeline()
307 self.link_pipeline_elements()
308 self.create_pipeline_callbacks()
309
310 global bus
311 bus = self.streampipe.get_bus()
312 bus.add_signal_watch()
313 bus.enable_sync_message_emission()
314 # Used to get messages that GStreamer emits.
315 bus.connect("message", self.on_message)
316
317 print('Pipeline creation state: successfully done.')
318 return self.streampipe
6db3115f
DT
319
320 def create_gstreamer_backup_pipeline(self):
321 """Creates a backup pipeline based on a webcam feed."""
322 print("CREATE HERE A BACKUP PIPELINE")
669383aa
DT
323
324 def on_message(self, bus, message):
325 t = message.type
326 if t == Gst.MessageType.EOS:
6db3115f 327 self.streampipe.set_state(Gst.State.NULL)
669383aa
DT
328 self.stream_button.set_label('Stream')
329 elif t == Gst.MessageType.ERROR:
330 err, debug = message.parse_error()
331 print ("Error: %s" % err, debug)
6db3115f
DT
332 self.streampipe.set_state(Gst.State.NULL)
333 self.create_gstreamer_backup_pipeline()
669383aa
DT
334
335 def stream_play(self):
336 self.streampipe.set_state(Gst.State.PLAYING)
337
338 def stream_stop(self):
339 self.streampipe.set_state(Gst.State.NULL)
340
341 def get_stream_state(self):
342 print(self.streampipe.get_state(self))
da450d89 343##[FIXME] return self.streampipe.get_state()
340ab727
DT
344
345 def set_filenames(self, string):
346 """Sets filename and location for each sink."""
347 filename = string
348 audio = PATHNAME + filename + '_AUDIO'
349 rawvideo = PATHNAME + filename + '_RAWVIDEO'
350 stream = PATHNAME + filename + '_STREAM'
351 rename(AUDIO_DEFAULT, audio)
352 rename(RAWVIDEO_DEFAULT, rawvideo)
353 rename(STREAM_DEFAULT, stream)
669383aa
DT
354
355def get_gstreamer_bus():
356 return bus
340ab727 357