diff --git a/voctocore/README.md b/voctocore/README.md index 6041e589..effe7046 100644 --- a/voctocore/README.md +++ b/voctocore/README.md @@ -34,12 +34,16 @@ Also, if enabled in Config, another Building-Block is chained after the Main-Mix / 10000… AVSource --> MirrorPort 13000… \-> Encoder* -> PreviewPort* 14000… + \ + \ + \--> Slides -> SlidesStreamBlanker*** -> SlidesStreamOutputPort** 15001 9999 Control-Server 9998 GstNetTimeProvider Network-Clock -*) only when [previews] enabled=true is configured -**) only when [stream-blanker] enabled=true is configured +*) only when [previews] enabled=true is configured +**) only when [stream-blanker] enabled=true is configured +***) only when [mix] slides_source_name=… is configured ```` ## Network Ports Listing diff --git a/voctocore/default-config.ini b/voctocore/default-config.ini index 3b689a94..26753ca9 100644 --- a/voctocore/default-config.ini +++ b/voctocore/default-config.ini @@ -5,6 +5,9 @@ audiocaps = audio/x-raw,format=S16LE,channels=2,layout=interleaved,rate=48000 ; tcp-ports will be 10000,10001,10002 sources = cam1,cam2,grabber +; setting this will create another stream-blanker which can be used to stream slides with the blanker-feature +;slides_source_name=grabber + ; number of stereo-streams used in the mix. the exact same number needs to be supplied from each ; source and is passed to each sink audiostreams = 1 diff --git a/voctocore/lib/pipeline.py b/voctocore/lib/pipeline.py index 3b527159..afd412d9 100644 --- a/voctocore/lib/pipeline.py +++ b/voctocore/lib/pipeline.py @@ -36,9 +36,14 @@ def __init__(self): outputs = [name + '_mixer'] if Config.getboolean('previews', 'enabled'): outputs.append(name + '_preview') + if Config.getboolean('mirrors', 'enabled'): outputs.append(name + '_mirror') + if Config.has_option('mix', 'slides_source_name') and \ + Config.get('mix', 'slides_source_name') == name: + outputs.append('slides_stream-blanker') + source = spawn_source(name, port, outputs=outputs) self.log.info('Creating AVSource %s as %s', name, source) self.sources.append(source) @@ -111,3 +116,8 @@ def __init__(self): port = 15000 self.log.info('Creating StreamBlanker-Output at tcp-port %u', port) self.streamout = AVRawOutput('stream-blanker_out', port) + + if Config.has_option('mix', 'slides_source_name'): + port = 15001 + self.log.info('Creating SlideStreamBlanker-Output at tcp-port %u', port) + self.slides_streamout = AVRawOutput('slides_stream-blanker_out', port) diff --git a/voctocore/lib/streamblanker.py b/voctocore/lib/streamblanker.py index b8100b8c..7bc49aa2 100644 --- a/voctocore/lib/streamblanker.py +++ b/voctocore/lib/streamblanker.py @@ -38,12 +38,32 @@ def __init__(self): vcaps=self.vcaps, ) + if Config.has_option('mix', 'slides_source_name'): + pipeline += """ + compositor name=vmix-slides ! + {vcaps} ! + queue ! + intervideosink channel=video_slides_stream-blanker_out + """.format( + vcaps=self.vcaps, + ) + + pipeline += """ + intervideosrc channel=video_slides_stream-blanker ! + {vcaps} ! + vmix-slides. + """.format( + vcaps=self.vcaps, + ) + for audiostream in range(0, Config.getint('mix', 'audiostreams')): # Audiomixer pipeline += """ audiomixer name=amix_{audiostream} ! {acaps} ! queue ! + tee name=amix_{audiostream}-tee ! + queue ! interaudiosink channel=audio_stream-blanker_out_stream{audiostream} """.format( @@ -51,6 +71,16 @@ def __init__(self): audiostream=audiostream, ) + if Config.has_option('mix', 'slides_source_name'): + pipeline += """ + amix_{audiostream}-tee. ! + queue ! + interaudiosink + channel=audio_slides_stream-blanker_out_stream{audiostream} + """.format( + audiostream=audiostream, + ) + # Source from the Main-Mix pipeline += """ interaudiosrc @@ -89,12 +119,24 @@ def __init__(self): pipeline += """ intervideosrc channel=video_stream-blanker-{name} ! {vcaps} ! + queue ! + tee name=video_stream-blanker-tee-{name} ! + queue ! vmix. - """.format( + """.format( name=name, vcaps=self.vcaps, ) + if Config.has_option('mix', 'slides_source_name'): + pipeline += """ + video_stream-blanker-tee-{name}. ! + queue ! + vmix-slides. + """.format( + name=name, + ) + self.log.debug('Creating Mixing-Pipeline:\n%s', pipeline) self.mixingPipeline = Gst.parse_launch(pipeline) self.mixingPipeline.use_clock(Clock) @@ -122,7 +164,9 @@ def on_error(self, bus, message): def applyMixerState(self): self.applyMixerStateAudio() - self.applyMixerStateVideo() + self.applyMixerStateVideo('vmix') + if Config.has_option('mix', 'slides_source_name'): + self.applyMixerStateVideo('vmix-slides') def applyMixerStateAudio(self): is_blanked = self.blankSource is not None @@ -141,14 +185,14 @@ def applyMixerStateAudio(self): 'volume', self.volume if is_blanked else 0.0) - def applyMixerStateVideo(self): - mixpad = (self.mixingPipeline.get_by_name('vmix') + def applyMixerStateVideo(self, mixername): + mixpad = (self.mixingPipeline.get_by_name(mixername) .get_static_pad('sink_0')) mixpad.set_property('alpha', int(self.blankSource is None)) for idx, name in enumerate(self.names): blankpad = (self.mixingPipeline - .get_by_name('vmix') + .get_by_name(mixername) .get_static_pad('sink_%u' % (idx + 1))) blankpad.set_property('alpha', int(self.blankSource == idx))