From af8c02a41f1523ae5837a8dba8bd89fb2639bf2e Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20Test=C3=A9?= Date: Wed, 24 Feb 2016 16:41:44 +0100 Subject: [PATCH] Creation of the raw video pipeline [still prototype] --- stream_2016/libre-streamer.py | 187 +++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 35 deletions(-) diff --git a/stream_2016/libre-streamer.py b/stream_2016/libre-streamer.py index 1ac1bc1..86dadc2 100755 --- a/stream_2016/libre-streamer.py +++ b/stream_2016/libre-streamer.py @@ -20,20 +20,33 @@ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # TODO list: # ---------- +# - Add a form to fill before start streaming (conf title, name, etc...) +# - Add a checkbox to enable/disable options (storing/streaming - storing only - stream only - etc...) +# - Add a function to get the ip address of the camera automatically (see github.com/paulmilliken) # - Create a module for the pipeline construction section to clarify the code -# - Create a module for the network configuration (ifconfig, stream server,etc) +# - Implement 2016 edition pipeline, see file 'gstream_pipeline_by_quidam' +# - Create a module for the network configuration (fan/cpu, ifconfig, stream server,etc) # - Generate a log file during runtime. (e.g. this will let you know if the network configuration # and the pipeline construction went well (or not)) # - Add an input source choice for the user (camera on IP or webcam) # - Add a VU-meter to check if audio feed is emitting signal -# - Investigate this error (at stream launching during prototyping phase): -# (libre-streamer.py:7856): Gdk-ERROR **: The program 'libre-streamer.py' received an X Window System error. -# This probably reflects a bug in the program. -# The error was 'BadIDChoice (invalid resource ID chosen for this connection)'. -# (Details: serial 592 error_code 14 request_code 1 (core protocol) minor_code 0) # - Add the FSF logo (need to do some pixel art) as an application icon +# - Add the FSF logo inside the streamer use the 'textoverlay' method in ElementFactory.make() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# INFO: run the following command in a terminal before launching libre-streamer to get a error log. +# GST_DEBUG=3,python:5,gnl*:5 ./libre-streamer.py | tee -a log 2>&1 +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +__author__ = 'David Testé' +__licence__ = 'GPLv3' +__version__ = 0.1 +__maintainer__ = 'David Testé' +__email__ = 'soonum@gnu.org' +__status__ = 'Prototype' + + import sys import gi @@ -43,9 +56,6 @@ from gi.repository import Gst from gi.repository import GdkX11 from gi.repository import GstVideo -##import networkinit -##import gstpipeline - class Streamgui(object): @@ -77,6 +87,11 @@ class Streamgui(object): self.win.set_position(Gtk.WindowPosition.CENTER) self.win.show_all() + self.xid = self.videowidget.get_property('window').get_xid() + self.connectsignals() + + def connectsignals(self): + """Connects signals with the methods""" bus = self.pipel.get_bus() bus.add_signal_watch() bus.enable_sync_message_emission() @@ -84,50 +99,154 @@ class Streamgui(object): bus.connect("message", self.on_message) # Used for connecting video to your application. bus.connect("sync-message::element", self.on_sync_message) + # Connect the rtpdepay signal + self.videosrc.connect("pad-added", self.on_pad_added_to_rtspsrc) + if self.decodebin: + self.decodebin.connect("pad-added", self.on_pad_added_to_decodebin) +## elif self.jpegdec: +## self.jpegdec.connect("pad-added", self.on_pad_added_to_jpegdec) + + def on_pad_added_to_rtspsrc(self, rtspsrc, pad): +## if self.decodebin: +## sinkpad = self.decodebin.get_static_pad('sink') +## elif self.rtpjpegdepay: +## sinkpad = self.rtpjpegdepay.get_static_pad('sink') + sinkpad = self.queuev_1.get_static_pad('sink') + pad.link(sinkpad) + print('[DEBUG] rtspsrc LINKED') - def constructpipeline (self): + def on_pad_added_to_decodebin(self, decodebin, pad): + screen_sinkpad = self.screensink.get_static_pad('sink') + pad.link(screen_sinkpad) + print('[DEBUG] decodebin LINKED') + def on_pad_added_to_jpegdec(self, jpegdec, pad): + screen_sinkpad = self.screensink.get_static_pad('sink') + pad.link(screen_sinkpad) + print('[DEBUG] decodebin LINKED') + + def constructpipeline (self): """Add and link elements in a GStreamer pipeline""" - # Create the pipelines instance. self.streampipe = Gst.Pipeline() -## self.storepipe_hi = Gst.Pipeline() -## self.storepipe_lo = Gst.Pipeline() # Define pipeline elements. - ## The next line WILL NOT display the camera's video feed - ## self.videosrc = Gst.ElementFactory.make('location', 'rtsp://192.168.48.2:554') - self.videosrc = Gst.ElementFactory.make('videotestsrc', 'source') - self.queue = Gst.ElementFactory.make('queue') - ## self.oggstreamsink = Gst.ElementFactory.make() - ## self.oggdisksink = Gst.ElementFactory.make() - - ## self.jpegdisksink = Gst.ElementFactory.make() - - self.screensink = Gst.ElementFactory.make('xvimagesink', 'rawfeed') - - ## self.webmdisksink = Gst.ElementFactory.make() - ## self.webmstreamsink = Gst.ElementFactory.make() - + self.videosrc = Gst.ElementFactory.make('rtspsrc', 'videosrc') + self.videosrc.set_property('location', 'rtsp://192.168.48.2:554') + self.videosrc.set_property('latency', 100) + + self.decodebin = Gst.ElementFactory.make('decodebin', 'decodebin') + +## Video source for testing purpose: +## self.videosrc = Gst.ElementFactory.make('videotestsrc', 'videosrc') + self.rtpjpegdepay = Gst.ElementFactory.make('rtpjpegdepay', 'rtpjpegdepay') + self.jpegdec = Gst.ElementFactory.make('jpegdec', 'jpegdec') + self.jpegdec.set_property('max-errors', -1) + self.mkvmux = Gst.ElementFactory.make('matroskamux', 'mkvmux') + self.tee_rawvideo = Gst.ElementFactory.make('tee', 'tee_rawvideo') + 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.disksink_rawvideo = Gst.ElementFactory.make('filesink') +#[TO DO]: File location has to be defined + self.disksink_rawvideo.set_property('location', 'popo_rawvideo') + self.screensink = Gst.ElementFactory.make('xvimagesink', 'screensink') + +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# IMPORTANT: +# for 'webmmux' element streamable=True MUST be set! +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + # Elements to test: + #--------------------------------------------------------------------------- + self.audiosrc = Gst.ElementFactory.make('pulsesrc', 'audiosrc') + self.vorbisenc = Gst.ElementFactory.make('vorbisenc', 'vorbisenc') +## scaling_caps = Gst.Caps('video/x-raw, width=640, height=360') + self.scaling = Gst.ElementFactory.make('videoscale', 'scaling') + 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) + self.webmmux = Gst.ElementFactory.make('webmmux', 'webmmux') + self.webmmux.set_property('streamable', True) + + self.tee_streamvideo = Gst.ElementFactory.make('tee', 'tee_streamvideo') + self.tee_streamaudio = Gst.ElementFactory.make('tee', 'tee_streamaudio') + self.queuea_1 = Gst.ElementFactory.make('queue', 'queuea_1') + self.queuea_2 = Gst.ElementFactory.make('queue', 'queuea_2') + + self.disksink_audio = Gst.ElementFactory.make('filesink') + self.disksink_audio.set_property('location', 'popo_audio') + self.disksink_stream = Gst.ElementFactory.make('filesink') + self.disksink_stream.set_property('location', 'popo_stream') + + 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', '') + 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', '') + #--------------------------------------------------------------------------- + # Add the elements to the pipeline. + # Test of the first two lines of quidam's pipeline: self.streampipe.add(self.videosrc) - self.streampipe.add(self.queue) +## self.streampipe.add(self.decodebin) + self.streampipe.add(self.queuev_1) + self.streampipe.add(self.rtpjpegdepay) + self.streampipe.add(self.queuev_2) + self.streampipe.add(self.jpegdec) + self.streampipe.add(self.tee_rawvideo) + self.streampipe.add(self.queuev_3) + self.streampipe.add(self.mkvmux) + self.streampipe.add(self.queuev_4) + self.streampipe.add(self.disksink_rawvideo) self.streampipe.add(self.screensink) - # Link the elements in the pipeline. - self.videosrc.link(self.queue) - self.queue.link(self.screensink) + + # Link the elements in the pipeline. +## self.videosrc.link(self.decodebin) + self.queuev_1.link(self.rtpjpegdepay) +## self.rtpjpegdepay.link(self.queuev_2) +## self.rtpjpegdepay.link(self.jpegdec) + self.rtpjpegdepay.link(self.tee_rawvideo) +## self.queuev_2.link(self.jpegdec) +## self.jpegdec.link(self.tee_rawvideo) +## self.jpegdec.link(self.queuev_3) + self.tee_rawvideo.link(self.queuev_2) + self.tee_rawvideo.link(self.jpegdec) +## self.tee_rawvideo.link(self.queuev_3) + self.queuev_2.link(self.mkvmux) + self.mkvmux.link(self.queuev_4) + self.queuev_4.link(self.disksink_rawvideo) +## self.decodebin.link(self.screensink) +## self.queuev_3.link(self.disksink_rawvideo) + self.jpegdec.link(self.queuev_3) + self.queuev_3.link(self.screensink) + return self.streampipe def on_message(self, bus, message): t = message.type if t == Gst.MessageType.EOS: - self.player.set_state(Gst.State.NULL) + self.pipel.set_state(Gst.State.NULL) + self.stream_button.set_label('Stream') elif t == Gst.MessageType.ERROR: - self.player.set_state(Gst.State.NULL) err, debug = message.parse_error() print ("Error: %s" % err, debug) + self.pipel.set_state(Gst.State.NULL) + self.stream_button.set_label('Stream') def on_sync_message(self, bus, message): @@ -144,8 +263,6 @@ class Streamgui(object): def on_stream_clicked(self, widget): - # Put here the script to launch streaming and start recording -## self.streampipe.uri_construct('rtsp', '://192.168.48.2:554') labelname = self.stream_button.get_label() if labelname == 'Stream': self.pipel.set_state(Gst.State.PLAYING) -- 2.25.1