From 2219fbe4bda924019080316b5718cbb6539f93d4 Mon Sep 17 00:00:00 2001
From: MaZderMind <peter@mazdermind.de>
Date: Mon, 25 Aug 2014 10:59:21 +0200
Subject: implement times-two distribution and main video mixer

---
 voctocore/default-config.ini |  1 +
 voctocore/lib/distributor.py | 33 +++++++++++++++++++++++++++
 voctocore/lib/pipeline.py    | 30 ++++++++++++++++---------
 voctocore/lib/quadmix.py     | 53 ++++++++++++++++++++++++++++----------------
 voctocore/lib/videomix.py    | 44 ++++++++++++++++++++++++++++++++++++
 5 files changed, 131 insertions(+), 30 deletions(-)
 create mode 100644 voctocore/lib/distributor.py
 create mode 100644 voctocore/lib/videomix.py

(limited to 'voctocore')

diff --git a/voctocore/default-config.ini b/voctocore/default-config.ini
index f00f91d..13bae74 100644
--- a/voctocore/default-config.ini
+++ b/voctocore/default-config.ini
@@ -19,5 +19,6 @@ audio=/video/dudel.m4a
 
 [mix]
 monitorcaps=video/x-raw,width=1024,height=576
+outputcaps=video/x-raw,width=1280,height=720
 
 [client]
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