X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=stream_2016%2Fgstconf.py;h=115c2d0ed53afcf1a5a878ead391a14a69b681d1;hb=6bb57e0692f67b785ecaed554b6497861df1221d;hp=9e69df60ad8232292f5b0f20dbd983c965012866;hpb=ba4fea24ed231612d0fa4d31694e7712f2386fb4;p=libre-streamer.git diff --git a/stream_2016/gstconf.py b/stream_2016/gstconf.py index 9e69df6..115c2d0 100755 --- a/stream_2016/gstconf.py +++ b/stream_2016/gstconf.py @@ -1,58 +1,89 @@ #!/usr/bin/env python3.4 +# -*- coding: utf-8 -*- -# This file is part of Libre-Streamer. +# This file is part of ABYSS. +# ABYSS Broadcast Your Streaming Successfully # -# Libre-Streamer is free software: you can redistribute it and/or modify +# ABYSS 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, +# ABYSS 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 . +# along with ABYSS. If not, see . # # Copyright (c) 2016 David Testé from os import rename from os import listdir +from os import path +from os import mkdir from time import localtime, strftime +import configparser 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' +DIR_NAME = 'FILES_RECORDED' +AUDIO_DEFAULT = './' + DIR_NAME + '/' + 'AUDIO_DEFAULT' +RAWVIDEO_DEFAULT = './' + DIR_NAME + '/' + 'RAWVIDEO_DEFAULT' +STREAM_DEFAULT = './' + DIR_NAME + '/' + 'STREAM_DEFAULT' BACKUP_SUFFIX = '_BACKUP' +FAILED_SUFFIX = '_FAILED_' +fail_counter = 1 AUDIO_BACKUP = AUDIO_DEFAULT + BACKUP_SUFFIX RAWVIDEO_BACKUP = RAWVIDEO_DEFAULT + BACKUP_SUFFIX STREAM_BACKUP = STREAM_DEFAULT + BACKUP_SUFFIX ERROR = '[ERROR] ' INFO = '[INFO] ' WARN = '[WARN] ' +CONFIG = '.abyss' +sources = {'RTSP_IP' : None, + 'AUDIO_INPUT' : None,} +sinks = {'AUDIO_OUTPUT' : None, + 'DIR': None, + 'STREAM_SERVER_IP' : None, + 'SERVER_PORT' : None, + 'PASSWORD' : None, + 'AUDIO_MOUNT' : None, + 'VIDEO_MOUNT' : None,} + +##AUDIO_INPUT = 'alsa_input.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00-CODEC.analog-stereo' +##AUDIO_OUTPUT = 'alsa_output.pci-0000_00_1b.0.analog-stereo' + +config = configparser.RawConfigParser() +if path.exists(CONFIG): + config.read(CONFIG) + try: + sources = {key : config.get('sources', key) for key in sources} + sinks = {key : config.get('sinks', key) for key in sinks} + except: + print(ERROR, gettime(), 'Failed to parse config file.') +else: + print(ERROR, gettime(), '".abyss" config file doesn\'t exist.') + +if not path.isdir(DIR_NAME): + mkdir(DIR_NAME) class New_user_pipeline(): def __init__(self, feed='main'): + self.rtsp_address = 'rtsp://' + sources['RTSP_IP'] 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('location', self.rtsp_address) self.videosrc.set_property('latency', 0) ## self.videosrc.set_property('debug', True) if self.feed == 'backup': @@ -72,7 +103,15 @@ class New_user_pipeline(): elif len(devices) == 1: return '/dev/video0' print(ERROR, gettime(), 'No webcam device found.') - + + def find_mixingdesk_device(self): + """Look out for the USB mixing desk device. + Product used here: Behringer XENYX Q1002USB. + """ + # shell cmd : 'pactl list | grep alsa_input' + # AUDIO_INPUT --> const used currently + pass + def create_pipeline_callbacks(self): """Callbacks to connect dynamically created pads.""" self.videosrc.connect('pad-added', self.on_pad_added_to_rtspsrc) @@ -85,7 +124,7 @@ class New_user_pipeline(): def create_audio_sources(self): """Create audio inputs from various sources.""" self.audiosrc = Gst.ElementFactory.make('pulsesrc', 'audiosrc') -## self.videosrc.set_property('latency', 0) + self.audiosrc.set_property('device', sources['AUDIO_INPUT']) def create_audiolevel_plugin(self): """Create audio level plugin to feed a vu-meter.""" @@ -95,32 +134,38 @@ class New_user_pipeline(): 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) - + if self.feed == 'backup': + self.disksink_rawvideo.set_property('location', RAWVIDEO_BACKUP) + self.disksink_audio.set_property('location', AUDIO_BACKUP) + self.disksink_stream.set_property('location', STREAM_BACKUP) + 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 local audio output (headphones): + self.audiosink = Gst.ElementFactory.make('pulsesink', 'audiosink') + self.audiosink.set_property('device', sinks['AUDIO_OUTPUT']) + self.audiosink.set_property('sync', False) # To icecast server: self.icecastsink_audio = Gst.ElementFactory.make('shout2send', 'icecastsink_audio') self.icecastsink_audio.set_property('sync', False) -## 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.ogg') - self.icecastsink_audio.set_property('password', 'thahw3Wiez') + self.icecastsink_audio.set_property('ip', sinks['STREAM_SERVER_IP']) + self.icecastsink_audio.set_property('port', int(sinks['SERVER_PORT'])) + self.icecastsink_audio.set_property('mount', sinks['AUDIO_MOUNT']) + self.icecastsink_audio.set_property('password', sinks['PASSWORD']) self.icecastsink_stream = Gst.ElementFactory.make('shout2send', 'icecastsink_stream') self.icecastsink_stream.set_property('sync', False) - self.icecastsink_stream.set_property('ip', 'live2.fsf.org') - self.icecastsink_stream.set_property('port', 80) - self.icecastsink_stream.set_property('mount', 'teststream.webm') - self.icecastsink_stream.set_property('password', 'thahw3Wiez') + self.icecastsink_stream.set_property('ip', sinks['STREAM_SERVER_IP']) + self.icecastsink_stream.set_property('port', int(sinks['SERVER_PORT'])) + self.icecastsink_stream.set_property('mount', sinks['VIDEO_MOUNT']) + self.icecastsink_stream.set_property('password', sinks['PASSWORD']) def create_payloader_elements(self): pass @@ -201,7 +246,6 @@ class New_user_pipeline(): 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') @@ -232,6 +276,8 @@ class New_user_pipeline(): # Output elements: self.create_filesink() self.create_streamsink() + if self.feed == 'test': + print('TEST OK...', end='') print('created') if self.feed == 'backup': print (INFO, @@ -242,38 +288,45 @@ class New_user_pipeline(): def add_elements_to_pipeline(self): print(INFO, gettime(), 'Pipeline creation state: adding elements... ', end='') + cond = self.feed != 'test' + # Inputs elements: self.streampipe.add(self.audiosrc) # Middle elements: self.streampipe.add(self.audiolevel) - self.streampipe.add(self.vorbisenc) - self.streampipe.add(self.vp8enc) - self.streampipe.add(self.mkvmux) - self.streampipe.add(self.oggmux) - self.streampipe.add(self.webmmux) - self.streampipe.add(self.tee_rawaudio) - self.streampipe.add(self.tee_rawvideo) - self.streampipe.add(self.tee_streamaudio) - self.streampipe.add(self.tee_streamfull) - 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.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) + self.streampipe.add(self.queuev_3) + if cond: + self.streampipe.add(self.vorbisenc) + self.streampipe.add(self.oggmux) + self.streampipe.add(self.queuea_2) + self.streampipe.add(self.queuea_3) + self.streampipe.add(self.vp8enc) + self.streampipe.add(self.mkvmux) + self.streampipe.add(self.webmmux) + self.streampipe.add(self.tee_rawaudio) + self.streampipe.add(self.tee_rawvideo) + self.streampipe.add(self.tee_streamaudio) + self.streampipe.add(self.tee_streamfull) + self.streampipe.add(self.queuev_2) + self.streampipe.add(self.queuev_4) + self.streampipe.add(self.queuev_5) + 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) - if self.feed == 'main': + if cond: + 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) + else: + self.streampipe.add(self.audiosink) + + if self.feed == 'main' or self.feed == 'test': # Inputs elements: self.streampipe.add(self.videosrc) # Middle elements: @@ -283,6 +336,8 @@ class New_user_pipeline(): self.streampipe.add(self.capsfilter) self.streampipe.add(self.tee_videodecoded) self.streampipe.add(self.queuev_1) + if self.feed == 'test': + print ('TEST OK...', end='') elif self.feed == 'backup': # Inputs elements: self.streampipe.add(self.videosrc_backup) @@ -294,62 +349,75 @@ class New_user_pipeline(): def link_pipeline_elements(self): """Link all elements with static pads.""" print(INFO, gettime(), 'Pipeline creation state: linking elements... ', end='') + cond = self.feed != 'test' + # 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) + if cond: + 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) + else: + self.queuea_1.link(self.audiosink) + # Video feed: - self.queuev_2.link(self.mkvmux) - self.mkvmux.link(self.queuev_4) - self.queuev_4.link(self.disksink_rawvideo) + if cond: + self.queuev_2.link(self.mkvmux) + self.mkvmux.link(self.queuev_4) + self.queuev_4.link(self.disksink_rawvideo) + else: + self.queuev_1.link(self.rtpjpegdepay) + self.rtpjpegdepay.link(self.jpegdec) + self.jpegdec.link(self.queuev_3) self.queuev_3.link(self.screensink) + # Stream (audio+video) feed: - self.vp8enc.link(self.queuev_5) - self.queuev_5.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) - if self.feed == 'main': - # linking here RTSP feed - self.queuev_1.link(self.rtpjpegdepay) - self.connect_tee(self.tee_rawvideo, - self.rtpjpegdepay, - self.queuev_2, - self.jpegdec,) - self.connect_tee(self.tee_videodecoded, - self.jpegdec, - self.queuev_3, - self.scaling,) - # Stream (video) feed: - self.scaling.link(self.capsfilter) - self.capsfilter.link(self.vp8enc) - - elif self.feed == 'backup': - # linking here backup feed (WEBCAM) - self.videosrc_backup.link(self.capsfilter_backup) - self.connect_tee(self.tee_rawvideo, - self.capsfilter_backup, - self.queuev_2, - self.queuev_3, - output_element_3=self.vp8enc) + if cond: + self.vp8enc.link(self.queuev_5) + self.queuev_5.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) + if self.feed == 'main': + # linking here RTSP feed + self.queuev_1.link(self.rtpjpegdepay) + self.connect_tee(self.tee_rawvideo, + self.rtpjpegdepay, + self.queuev_2, + self.jpegdec,) + self.connect_tee(self.tee_videodecoded, + self.jpegdec, + self.queuev_3, + self.scaling,) + # Stream (video) feed: + self.scaling.link(self.capsfilter) + self.capsfilter.link(self.vp8enc) + elif self.feed == 'backup': + # linking here backup feed (WEBCAM) + self.videosrc_backup.link(self.capsfilter_backup) + self.connect_tee(self.tee_rawvideo, + self.capsfilter_backup, + self.queuev_2, + self.queuev_3, + output_element_3=self.vp8enc) ## self.capsfilter_backup.link(self.queuev_3) - # Stream (video) feed: - print('BACKUP OK...', end='') + print('BACKUP OK...', end='') + if not cond: + print('TEST OK...', end='') print('linked') def create_gstreamer_pipeline(self): @@ -359,7 +427,7 @@ class New_user_pipeline(): # Setting-up: self.add_elements_to_pipeline() self.link_pipeline_elements() - if self.feed == 'main': + if self.feed == 'main' or self.feed == 'test': self.create_pipeline_callbacks() global bus @@ -373,46 +441,60 @@ class New_user_pipeline(): return self.streampipe 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) def stream_play(self): self.streampipe.set_state(Gst.State.PLAYING) if self.feed == 'backup': print(WARN, gettime(), 'Backup pipeline started.') - print(INFO, gettime(), 'PLAYING State resquested') + print(INFO, gettime(), 'PLAYING State resquested.') def stream_stop(self): self.streampipe.set_state(Gst.State.NULL) - print(INFO, gettime(), 'STOPPED State resquested') + print(INFO, gettime(), 'STOPPED State resquested.') - def get_stream_state(self): - print(self.streampipe.get_state(self)) -##[FIXME] return self.streampipe.get_state() - - def set_filenames(self, string): + def set_filenames(self, string, streamfailed=False): """Sets filename and location for each sink.""" + global fail_counter filename = string - audio = PATHNAME + filename + '_AUDIO' - rawvideo = PATHNAME + filename + '_RAWVIDEO' - stream = PATHNAME + filename + '_STREAM' + audio = './' + DIR_NAME + '/' + filename + '_AUDIO' + rawvideo = './' + DIR_NAME + '/' + filename + '_RAWVIDEO' + stream = './' + DIR_NAME + '/' + filename + '_STREAM' if self.feed == 'main': - rename(AUDIO_DEFAULT, audio) - rename(RAWVIDEO_DEFAULT, rawvideo) - rename(STREAM_DEFAULT, stream) + if streamfailed and filename: + audio = audio + FAILED_SUFFIX + str(fail_counter) + rawvideo = rawvideo + FAILED_SUFFIX + str(fail_counter) + stream = stream + FAILED_SUFFIX + str(fail_counter) + self.rename_files(audio, rawvideo, stream) + fail_counter += 1 + elif streamfailed: + audio = AUDIO_DEFAULT + FAILED_SUFFIX + str(fail_counter) + rawvideo = RAWVIDEO_DEFAULT + FAILED_SUFFIX + str(fail_counter) + stream = STREAM_DEFAULT + FAILED_SUFFIX + str(fail_counter) + self.rename_files(audio, rawvideo, stream) + fail_counter += 1 + else: + self.rename_files(audio, rawvideo, stream) elif self.feed == 'backup': +## print('INSIDE BACKUP RENAMING') rename(AUDIO_BACKUP, audio) rename(RAWVIDEO_BACKUP, rawvideo) rename(STREAM_BACKUP, stream) + print(INFO, gettime(), 'Audio file written on disk.') + print(INFO, gettime(), 'Raw video file written on disk.') + print(INFO, gettime(), 'Streamed file written on disk.') + + def rename_files(self, audio_name, rawvideo_name, stream_name): + rename(AUDIO_DEFAULT, audio_name) + rename(RAWVIDEO_DEFAULT, rawvideo_name) + rename(STREAM_DEFAULT, stream_name) + def get_gstreamer_bus(): return bus