# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 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
from gi.repository import GdkX11
from gi.repository import GstVideo
-##import networkinit
-##import gstpipeline
-
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()
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):
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)