From 2219fbe4bda924019080316b5718cbb6539f93d4 Mon Sep 17 00:00:00 2001 From: MaZderMind Date: Mon, 25 Aug 2014 10:59:21 +0200 Subject: implement times-two distribution and main video mixer --- voctocore/lib/distributor.py | 33 +++++++++++++++++++++++++++ voctocore/lib/pipeline.py | 30 ++++++++++++++++--------- voctocore/lib/quadmix.py | 53 ++++++++++++++++++++++++++++---------------- voctocore/lib/videomix.py | 44 ++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 30 deletions(-) create mode 100644 voctocore/lib/distributor.py create mode 100644 voctocore/lib/videomix.py (limited to 'voctocore/lib') diff --git a/voctocore/lib/distributor.py b/voctocore/lib/distributor.py new file mode 100644 index 0000000..8282839 --- /dev/null +++ b/voctocore/lib/distributor.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +import time, logging +from gi.repository import GLib, Gst + +from lib.config import Config + +class TimesTwoDistributor(Gst.Bin): + log = logging.getLogger('TimesTwoDistributor') + + def __init__(self): + super().__init__() + + self.tee = Gst.ElementFactory.make('tee', None) + self.queue_a = Gst.ElementFactory.make('queue', 'queue-a') + self.queue_b = Gst.ElementFactory.make('queue', 'queue-b') + + self.add(self.tee) + self.add(self.queue_a) + self.add(self.queue_b) + + self.tee.link(self.queue_a) + self.tee.link(self.queue_b) + + # Add Ghost Pads + self.add_pad( + Gst.GhostPad.new('sink', self.tee.get_static_pad('sink')) + ) + self.add_pad( + Gst.GhostPad.new('src_a', self.queue_a.get_static_pad('src')) + ) + self.add_pad( + Gst.GhostPad.new('src_b', self.queue_b.get_static_pad('src')) + ) diff --git a/voctocore/lib/pipeline.py b/voctocore/lib/pipeline.py index 112ac3e..b89d2af 100644 --- a/voctocore/lib/pipeline.py +++ b/voctocore/lib/pipeline.py @@ -8,9 +8,9 @@ from lib.controlserver import controlServerEntrypoint # import library components from lib.config import Config from lib.quadmix import QuadMix -# from lib.videomix import VideoMix +from lib.videomix import VideoMix # from lib.audiomix import AudioMix -# from lib.distributor import TimesTwoDistributor +from lib.distributor import TimesTwoDistributor from lib.shmsrc import FailsafeShmSrc class Pipeline(Gst.Pipeline): @@ -27,8 +27,8 @@ class Pipeline(Gst.Pipeline): self.quadmixer = QuadMix() self.add(self.quadmixer) - # self.videomixer = VideoMix() - # self.add(self.videomixer) + self.videomixer = VideoMix() + self.add(self.videomixer) # self.audiomixer = AudioMix() # self.add(self.audiomixer) @@ -51,10 +51,13 @@ class Pipeline(Gst.Pipeline): self.log.info('Creating video-source %s at socket-path %s', name, socket) sourcebin = FailsafeShmSrc(socket) self.add(sourcebin) - self.quadmixer.add_source(sourcebin) - # distributor = TimesTwoDistributor(sourcebin) - # self.add(distributor) + distributor = TimesTwoDistributor() + self.add(distributor) + sourcebin.link(distributor) + + self.quadmixer.add_source(distributor) + self.videomixer.add_source(distributor) # distributor.link(self.quadmixer) # distributor.link(self.videomixer) @@ -68,14 +71,18 @@ class Pipeline(Gst.Pipeline): # tell the quadmix that this were all sources and no more sources will come after this self.quadmixer.finalize() + self.quadmixer.set_active(0) + self.videomixer.set_active(0) + self.quadmixsink = Gst.ElementFactory.make('autovideosink', 'quadmixsink') self.quadmixsink.set_property('sync', False) self.add(self.quadmixsink) self.quadmixer.link(self.quadmixsink) - # self.videosink = Gst.ElementFactory.make('autovideosink', 'videosink') - # self.add(self.videosink) - # self.videomixer.link(self.videosink) + self.videosink = Gst.ElementFactory.make('autovideosink', 'videosink') + self.videosink.set_property('sync', False) + self.add(self.videosink) + self.videomixer.link(self.videosink) # self.audiosink = Gst.ElementFactory.make('autoaudiosink', 'audiosink') # self.add(self.audiosink) @@ -349,7 +356,8 @@ class Pipeline(Gst.Pipeline): self.log.info("switching quadmix to video-source %u", idx) self.quadmixer.set_active(idx) - # todo: switch main switcher + self.videomixer.set_active(idx) + @controlServerEntrypoint def fadeVideo(self, videosource): diff --git a/voctocore/lib/quadmix.py b/voctocore/lib/quadmix.py index 5115919..6a9384d 100644 --- a/voctocore/lib/quadmix.py +++ b/voctocore/lib/quadmix.py @@ -68,8 +68,24 @@ class QuadMix(Gst.Bin): # iterate over all video-sources for idx, videosource in enumerate(self.sources): + # create a sub-preview-bin + previewbin = QuadMixPreview() + self.add(previewbin) + self.previewbins.append(previewbin) + + previewsink = previewbin.get_static_pad('sink') + previewsrc = previewbin.get_static_pad('src') + + srcpad = videosource.get_compatible_pad(previewsink, None) + #srcpad.link(previewsink) # linking ghost pads + print(videosource.link(previewbin)) + + sinkpad = self.mixer.get_request_pad('sink_%u') + #previewsrc.link(sinkpad) # linking ghost pads + print(previewbin.link(self.mixer)) + # query the video-source caps and extract its size - caps = videosource.get_static_pad('src').query_caps(None) + caps = srcpad.query_caps(None) capsstruct = caps.get_structure(0) srcSize = ( capsstruct.get_int('width')[1], @@ -93,20 +109,11 @@ class QuadMix(Gst.Bin): idx, srcSize[0], srcSize[1], f, scaleSize[0], scaleSize[1], cellSize[0], cellSize[1], place[0], place[1], coord[0], coord[1]) # request a pad from the quadmixer and configure x/y position - sinkpad = self.mixer.get_request_pad('sink_%u') sinkpad.set_property('xpos', round(coord[0])) sinkpad.set_property('ypos', round(coord[1])) - # create a sub-preview-bin - previewbin = QuadMixPreview(idx, scaleSize) - self.add(previewbin) - self.previewbins.append(previewbin) - - # link videosource to input of previewbin - videosource.link(previewbin) - - # link the output of the preview-bin to the mixer - previewbin.get_static_pad('src').link(sinkpad) + previewbin.set_size(scaleSize) + previewbin.set_idx(idx) # increment grid position place[0] += 1 @@ -122,23 +129,21 @@ class QuadMixPreview(Gst.Bin): log = logging.getLogger('QuadMixPreview') strokeWidth = 5 - def __init__(self, idx, scaleSize): + def __init__(self): super().__init__() self.scale = Gst.ElementFactory.make('videoscale', 'scale') + self.caps = Gst.ElementFactory.make('capsfilter', 'caps') self.cropbox = Gst.ElementFactory.make('videobox', 'cropbox') self.strokebox = Gst.ElementFactory.make('videobox', 'strokebox') self.textoverlay = Gst.ElementFactory.make('textoverlay', 'textoverlay') self.add(self.scale) + self.add(self.caps) self.add(self.cropbox) self.add(self.strokebox) self.add(self.textoverlay) - caps = Gst.Caps.new_empty_simple('video/x-raw') - caps.set_value('width', round(scaleSize[0])) - caps.set_value('height', round(scaleSize[1])) - self.strokebox.set_property('fill', 'green') self.textoverlay.set_property('color', 0xFFFFFFFF) @@ -148,7 +153,8 @@ class QuadMixPreview(Gst.Bin): self.textoverlay.set_property('ypad', 5) self.textoverlay.set_property('font-desc', 'sans 35') - self.scale.link_filtered(self.cropbox, caps) + self.scale.link(self.caps) + self.caps.link(self.cropbox) self.cropbox.link(self.strokebox) self.strokebox.link(self.textoverlay) @@ -162,11 +168,20 @@ class QuadMixPreview(Gst.Bin): Gst.GhostPad.new('src', self.textoverlay.get_static_pad('src')) ) + def set_size(self, scaleSize): + caps = Gst.Caps.new_empty_simple('video/x-raw') + caps.set_value('width', round(scaleSize[0])) + caps.set_value('height', round(scaleSize[1])) + self.caps.set_property('caps', caps) + + def set_idx(self, idx): + self.textoverlay.set_property('text', str(idx)) + def set_active(self, active): self.log.info("switching active-state to %u", active) for side in ('top', 'left', 'right', 'bottom'): self.cropbox.set_property(side, self.strokeWidth if active else 0) self.strokebox.set_property(side, -self.strokeWidth if active else 0) - def setColor(self, color): + def set_color(self, color): self.strokebox.set_property('fill', color) diff --git a/voctocore/lib/videomix.py b/voctocore/lib/videomix.py new file mode 100644 index 0000000..d32dd90 --- /dev/null +++ b/voctocore/lib/videomix.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +import time, logging +from gi.repository import GLib, Gst + +from lib.config import Config + +class VideoMix(Gst.Bin): + log = logging.getLogger('VideoMix') + sinkpads = [] + + def __init__(self): + super().__init__() + + self.mixer = Gst.ElementFactory.make('videomixer', 'mixer') + self.scale = Gst.ElementFactory.make('videoscale', 'scale') + self.conv = Gst.ElementFactory.make('videoconvert', 'conv') + + self.add(self.mixer) + self.add(self.scale) + self.add(self.conv) + + caps = Gst.Caps.from_string(Config.get('mix', 'outputcaps')) + self.mixer.link_filtered(self.scale, caps) + self.scale.link(self.conv) + + self.add_pad( + Gst.GhostPad.new('src', self.conv.get_static_pad('src')) + ) + + # I don't know how to create a on-request ghost-pad + def add_source(self, src): + self.log.info('adding source %s', src.get_name()) + sinkpad = self.mixer.get_request_pad('sink_%u') + sinkpad.set_property('alpha', 1) + self.sinkpads.append(sinkpad) + + #srcpad = src.get_compatible_pad(sinkpad, None) + #srcpad.link(sinkpad) + src.link(self.mixer) # linking ghost pads + + def set_active(self, target): + self.log.info('setting source #%u active', target) + for idx, sinkpad in enumerate(self.sinkpads): + sinkpad.set_property('alpha', int(target == idx)) -- cgit v1.2.3