#!/usr/bin/env python3.4 # This file is part of Libre-Streamer. # # Libre-Streamer is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Libre-Streamer is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Libre-Streamer. If not, see . # # Copyright (c) 2016 David Testé from os import rename from os import listdir import gi from gi.repository import Gst from gi.repository import GstVideo # Pathname has to be defined PATHNAME = '' AUDIO_DEFAULT = PATHNAME + 'AUDIO_DEFAULT' RAWVIDEO_DEFAULT = PATHNAME + 'RAWVIDEO_DEFAULT' STREAM_DEFAULT = PATHNAME + 'STREAM_DEFAULT' class New_user_pipeline(): def __init__(self, feed='main'): self.feed = feed if self.feed == 'main': self.user_pipeline = self.create_gstreamer_pipeline() elif self.feed == 'backup': self.user_pipeline = self.create_gstreamer_pipeline(feed='backup') def create_video_sources(self): """Create video inputs from various sources.""" self.videosrc = Gst.ElementFactory.make('rtspsrc', 'videosrc') self.videosrc.set_property('location', 'rtsp://192.168.48.2:554') self.videosrc.set_property('latency', 0) if self.feed == 'backup': self.videosrc_backup = Gst.ElementFactory.make('v4l2src', 'videosrc_backup') device_location = self.find_webcam_device() print ('[INFO] Webcam device location: ', device_location) self.videosrc_backup.set_property('device', device_location) def find_webcam_device(self): """Look out for the USB webcam device.""" devices = [dev for dev in listdir('/dev/') if 'video' in dev] for item in devices: # In case of computer having a built-in webcam if item != 'video0' and len(devices) > 1: return '/dev/' + item # Without built-in webcam elif len(devices) == 1: return '/dev/video0' print('[ERROR] No webcam device found.') def create_pipeline_callbacks(self): """Callbacks to connect dynamically created pads.""" self.videosrc.connect('pad-added', self.on_pad_added_to_rtspsrc) def on_pad_added_to_rtspsrc(self, rtspsrc, pad): """Connect the dynamic 'src'pad of an RTSP source.""" sinkpad = self.queuev_1.get_static_pad('sink') pad.link(sinkpad) def create_audio_sources(self): """Create audio inputs from various sources.""" self.audiosrc = Gst.ElementFactory.make('pulsesrc', 'audiosrc') ## self.videosrc.set_property('latency', 0) def create_audiolevel_plugin(self): """Create audio level plugin to feed a vu-meter.""" self.audiolevel = Gst.ElementFactory.make('level', 'audiolevel') self.audiolevel.set_property('interval', 200000000) def create_filesink(self): """Create storable output elements.""" self.disksink_rawvideo = Gst.ElementFactory.make('filesink') #[TO DO]: File location has to be defined self.disksink_rawvideo.set_property('location', RAWVIDEO_DEFAULT) self.disksink_audio = Gst.ElementFactory.make('filesink') self.disksink_audio.set_property('location', AUDIO_DEFAULT) self.disksink_stream = Gst.ElementFactory.make('filesink') self.disksink_stream.set_property('location', STREAM_DEFAULT) def create_streamsink(self): """Create streamable output elements.""" # To local screen: self.screensink = Gst.ElementFactory.make('xvimagesink', 'screensink') self.screensink.set_property('sync', False) # To icecast server: self.icecastsink_audio = Gst.ElementFactory.make('shout2send', 'icecastsink_audio') ## Configuration should be written on a file locally to keep safe private addresses self.icecastsink_audio.set_property('ip', 'live2.fsf.org') self.icecastsink_audio.set_property('port', 80) self.icecastsink_audio.set_property('mount', 'testaudio.ogv') self.icecastsink_audio.set_property('password', 'thahw3Wiez') self.icecastsink_stream = Gst.ElementFactory.make('shout2send', 'icecastsink_stream') self.icecastsink_stream.set_property('ip', 'live2.fsf.org') self.icecastsink_stream.set_property('port', 80) self.icecastsink_stream.set_property('mount', 'teststream.ogv') self.icecastsink_stream.set_property('password', 'thahw3Wiez') def create_payloader_elements(self): pass def create_depayloader_elements(self): self.rtpjpegdepay = Gst.ElementFactory.make('rtpjpegdepay', 'rtpjpegdepay') def create_encoder_elements(self): # Audio encoders: self.vorbisenc = Gst.ElementFactory.make('vorbisenc', 'vorbisenc') # Video encoders: self.vp8enc = Gst.ElementFactory.make('vp8enc', 'vp8enc') self.vp8enc.set_property('min_quantizer', 1) self.vp8enc.set_property('max_quantizer', 13) self.vp8enc.set_property('cpu-used', 5) self.vp8enc.set_property('deadline', 42000) self.vp8enc.set_property('threads', 2) self.vp8enc.set_property('sharpness', 7) def create_decoder_elements(self): self.jpegdec = Gst.ElementFactory.make('jpegdec', 'jpegdec') self.jpegdec.set_property('max-errors', -1) def create_muxer_elements(self): self.oggmux = Gst.ElementFactory.make('oggmux', 'oggmux') self.mkvmux = Gst.ElementFactory.make('matroskamux', 'mkvmux') self.webmmux = Gst.ElementFactory.make('webmmux', 'webmmux') self.webmmux.set_property('streamable', True) def create_demuxer_elements(self): pass def create_filtering_elements(self): self.scaling = Gst.ElementFactory.make('videoscale', 'scaling') caps = Gst.caps_from_string('video/x-raw, width=(int)640, height=(int)360') self.capsfilter = Gst.ElementFactory.make('capsfilter', 'capsfilter') self.capsfilter.set_property('caps', caps) caps_backup = Gst.caps_from_string('video/x-raw, width=(int)640, height=(int)360') self.capsfilter_backup = Gst.ElementFactory.make('capsfilter', 'capsfilter_backup') self.capsfilter_backup.set_property('caps', caps_backup) def create_tee_elements(self): """Create tee elements to divide feeds.""" self.tee_rawvideo = Gst.ElementFactory.make('tee', 'tee_rawvideo') self.tee_videodecoded = Gst.ElementFactory.make('tee', 'tee_videodecoded') self.tee_streamfull = Gst.ElementFactory.make('tee', 'tee_streamfull') self.tee_rawaudio = Gst.ElementFactory.make('tee', 'tee_rawaudio') self.tee_streamaudio = Gst.ElementFactory.make('tee', 'tee_streamaudio') def connect_tee(self, tee_element, input_element, output_element_1, output_element_2,): """Links input and outputs of a given tee element.""" # Find a way to check if the element given are in the pipeline # then pass the result to the 'if' statement. ## argcheck = [True for arg in locals() if arg in 'the_list_of_elements_added'] ## print('[DEBUG] ArgList check: ', argcheck) ## if False not in argcheck if True: input_element.link(tee_element) tee_element.link(output_element_1) tee_element.link(output_element_2) else: print('[ERROR] Couldn\'t link the tee. Element(s) probably not in the pipeline ') def create_queues(self): # For video feed: self.queuev_1 = Gst.ElementFactory.make('queue', 'queuev_1') self.queuev_2 = Gst.ElementFactory.make('queue', 'queuev_2') self.queuev_3 = Gst.ElementFactory.make('queue', 'queuev_3') self.queuev_4 = Gst.ElementFactory.make('queue', 'queuev_4') self.queuev_5 = Gst.ElementFactory.make('queue', 'queuev_5') self.queuev_6 = Gst.ElementFactory.make('queue', 'queuev_6') # For audio feed: self.queuea_1 = Gst.ElementFactory.make('queue', 'queuea_1') self.queuea_2 = Gst.ElementFactory.make('queue', 'queuea_2') self.queuea_3 = Gst.ElementFactory.make('queue', 'queuea_3') self.queuea_4 = Gst.ElementFactory.make('queue', 'queuea_4') self.queuea_5 = Gst.ElementFactory.make('queue', 'queuea_5') # For audio+video muxer: self.queuem_1 = Gst.ElementFactory.make('queue', 'queuem_1') self.queuem_2 = Gst.ElementFactory.make('queue', 'queuem_2') def create_pipeline_elements(self): print('Pipeline creation state: creating elements... ', end='') # Inputs elements: self.create_video_sources() self.create_audio_sources() # Middle elements: self.create_audiolevel_plugin() self.create_payloader_elements() self.create_depayloader_elements() self.create_encoder_elements() self.create_decoder_elements() self.create_muxer_elements() self.create_filtering_elements() self.create_tee_elements() self.create_queues() # Output elements: self.create_filesink() self.create_streamsink() print('created') def add_elements_to_pipeline(self, feed='main'): print('Pipeline creation state: adding elements... ', end='') if feed == 'main': # Add here the elments associated with the RTSP feed # Inputs elements: self.streampipe.add(self.videosrc) self.streampipe.add(self.audiosrc) # Middle elements: self.streampipe.add(self.audiolevel) self.streampipe.add(self.rtpjpegdepay) self.streampipe.add(self.jpegdec) self.streampipe.add(self.tee_rawvideo) self.streampipe.add(self.mkvmux) self.streampipe.add(self.vorbisenc) self.streampipe.add(self.oggmux) self.streampipe.add(self.scaling) self.streampipe.add(self.capsfilter) self.streampipe.add(self.vp8enc) self.streampipe.add(self.webmmux) self.streampipe.add(self.tee_rawaudio) self.streampipe.add(self.tee_streamaudio) self.streampipe.add(self.tee_videodecoded) self.streampipe.add(self.tee_streamfull) self.streampipe.add(self.queuev_1) self.streampipe.add(self.queuev_2) self.streampipe.add(self.queuev_3) self.streampipe.add(self.queuev_4) self.streampipe.add(self.queuev_5) self.streampipe.add(self.queuev_6) self.streampipe.add(self.queuea_1) self.streampipe.add(self.queuea_2) self.streampipe.add(self.queuea_3) self.streampipe.add(self.queuea_4) self.streampipe.add(self.queuea_5) self.streampipe.add(self.queuem_1) self.streampipe.add(self.queuem_2) # Outputs elements: self.streampipe.add(self.screensink) self.streampipe.add(self.disksink_rawvideo) self.streampipe.add(self.disksink_audio) self.streampipe.add(self.disksink_stream) self.streampipe.add(self.icecastsink_audio) self.streampipe.add(self.icecastsink_stream) elif feed == 'backup': # Add here the elments associated with the WEBCAM feed self.streampipe.add(self.videosrc_backup) self.streampipe.add(self.capsfilter_backup) self.streampipe.add(self.queuev_1) self.streampipe.add(self.screensink) print ('BACKUP OK...', end='') print('added') def link_pipeline_elements(self, feed='main'): """Link all elements with static pads.""" print('Pipeline creation state: linking elements... ', end='') if feed == 'main': # linking here RTSP feed # Video feed: self.queuev_1.link(self.rtpjpegdepay) self.connect_tee(self.tee_rawvideo, self.rtpjpegdepay, self.queuev_2, self.jpegdec,) self.queuev_2.link(self.mkvmux) self.mkvmux.link(self.queuev_4) self.queuev_4.link(self.disksink_rawvideo) self.connect_tee(self.tee_videodecoded, self.jpegdec, self.queuev_3, self.scaling,) self.queuev_3.link(self.screensink) # Audio feed: self.audiosrc.link(self.audiolevel) self.audiolevel.link(self.queuea_1) self.queuea_1.link(self.vorbisenc) self.connect_tee(self.tee_rawaudio, self.vorbisenc, self.queuea_2, self.queuea_5,) self.queuea_2.link(self.oggmux) self.connect_tee(self.tee_streamaudio, self.oggmux, self.queuea_3, self.queuea_4,) self.queuea_3.link(self.disksink_audio) self.queuea_4.link(self.icecastsink_audio) self.queuea_5.link(self.webmmux) # Stream (audio+video) feed: self.scaling.link(self.capsfilter) self.capsfilter.link(self.vp8enc) self.vp8enc.link(self.queuev_6) self.queuev_6.link(self.webmmux) self.connect_tee(self.tee_streamfull, self.webmmux, self.queuem_1, self.queuem_2,) self.queuem_1.link(self.disksink_stream) self.queuem_2.link(self.icecastsink_stream) elif feed == 'backup': # linking here backup feed (WEBCAM) self.videosrc_backup.link(self.capsfilter_backup) self.capsfilter_backup.link(self.queuev_1) self.queuev_1.link(self.screensink) print('BACKUP OK...', end='') print('linked') def create_gstreamer_pipeline(self, feed='main'): # New empty pipeline: self.streampipe = Gst.Pipeline() self.create_pipeline_elements() # Setting-up: if feed == 'main': self.add_elements_to_pipeline() self.link_pipeline_elements() self.create_pipeline_callbacks() elif feed == 'backup': self.add_elements_to_pipeline(feed='backup') self.link_pipeline_elements(feed='backup') global bus bus = self.streampipe.get_bus() bus.add_signal_watch() bus.enable_sync_message_emission() # Used to get messages that GStreamer emits. bus.connect("message", self.on_message) print('Pipeline creation state: successfully done.') return self.streampipe def create_gstreamer_backup_pipeline(self): """Creates a backup pipeline based on a webcam feed.""" print("CREATE HERE A BACKUP PIPELINE") def on_message(self, bus, message): # ## print("[MESSAGE]", message.get_structure().get_name()) # [DEBUG] # t = message.type if t == Gst.MessageType.EOS: self.streampipe.set_state(Gst.State.NULL) elif t == Gst.MessageType.ERROR: err, debug = message.parse_error() print ("Error: %s" % err, debug) self.streampipe.set_state(Gst.State.NULL) ## self.create_gstreamer_backup_pipeline() ## return 'ERROR' def stream_play(self): self.streampipe.set_state(Gst.State.PLAYING) print('[INFO] PLAYING State resquested') def stream_stop(self): self.streampipe.set_state(Gst.State.NULL) def get_stream_state(self): print(self.streampipe.get_state(self)) ##[FIXME] return self.streampipe.get_state() def set_filenames(self, string): """Sets filename and location for each sink.""" filename = string audio = PATHNAME + filename + '_AUDIO' rawvideo = PATHNAME + filename + '_RAWVIDEO' stream = PATHNAME + filename + '_STREAM' rename(AUDIO_DEFAULT, audio) rename(RAWVIDEO_DEFAULT, rawvideo) rename(STREAM_DEFAULT, stream) def get_gstreamer_bus(): return bus