summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2017-02-28 15:10:39 +0100
committerJonas Smedegaard <dr@jones.dk>2017-02-28 15:10:39 +0100
commitcb42a9ad193179c527b44eccd3ca3ac59d573386 (patch)
tree8967af004209c8d7f159e45e5807979c7ca5953f
parent6ce57b47f050c39fbf28f4af639ef19608a74cfa (diff)
Add composition Matrix Two-by-two.
-rwxr-xr-xexample-scripts/control-server/demo-cycle-modes.sh3
-rwxr-xr-xexample-scripts/control-server/set-audio-cam3.sh9
-rwxr-xr-xexample-scripts/control-server/set-audio-cam4.sh9
-rwxr-xr-xexample-scripts/control-server/set-composite-matrix-two-by-two.sh9
-rwxr-xr-xexample-scripts/gstreamer/source-videotestsrc-as-cam3.sh19
-rwxr-xr-xexample-scripts/gstreamer/source-videotestsrc-as-cam4.sh19
-rwxr-xr-xexample-scripts/misc/benchmark.sh2
-rwxr-xr-xexample-scripts/misc/demo-local.sh2
-rwxr-xr-xexample-scripts/misc/perf.sh2
-rw-r--r--example-scripts/systemd-units/voctomix-cam3-source.service14
-rw-r--r--example-scripts/systemd-units/voctomix-cam4-source.service14
-rw-r--r--voctocore/default-config.ini11
-rw-r--r--voctocore/lib/videomix.py81
-rw-r--r--voctogui/lib/shortcuts.py3
-rw-r--r--voctogui/lib/toolbar/composition.py3
-rw-r--r--voctogui/ui/composite-matrix-two-by-two.svg95
-rw-r--r--voctogui/ui/voctogui.ui19
17 files changed, 311 insertions, 3 deletions
diff --git a/example-scripts/control-server/demo-cycle-modes.sh b/example-scripts/control-server/demo-cycle-modes.sh
index 7440cf1..ab9cdbd 100755
--- a/example-scripts/control-server/demo-cycle-modes.sh
+++ b/example-scripts/control-server/demo-cycle-modes.sh
@@ -35,6 +35,9 @@ while true; do
./set-video-cam2.sh
./set-audio-cam2.sh
sleep 10
+ echo "composite-matrix-two-by-two"
+ ./set-composite-matrix-two-by-two.sh
+ sleep 10
echo "composite-picture-in-picture"
./set-composite-picture-in-picture.sh
sleep 10
diff --git a/example-scripts/control-server/set-audio-cam3.sh b/example-scripts/control-server/set-audio-cam3.sh
new file mode 100755
index 0000000..b591879
--- /dev/null
+++ b/example-scripts/control-server/set-audio-cam3.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+confdir="`dirname "$0"`/../"
+. $confdir/default-config.sh
+if [ -f $confdir/config.sh ]; then
+ . $confdir/config.sh
+fi
+
+echo set_audio cam3 | nc -q0 $MIXHOST 9999
diff --git a/example-scripts/control-server/set-audio-cam4.sh b/example-scripts/control-server/set-audio-cam4.sh
new file mode 100755
index 0000000..2dd26a8
--- /dev/null
+++ b/example-scripts/control-server/set-audio-cam4.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+confdir="`dirname "$0"`/../"
+. $confdir/default-config.sh
+if [ -f $confdir/config.sh ]; then
+ . $confdir/config.sh
+fi
+
+echo set_audio cam4 | nc -q0 $MIXHOST 9999
diff --git a/example-scripts/control-server/set-composite-matrix-two-by-two.sh b/example-scripts/control-server/set-composite-matrix-two-by-two.sh
new file mode 100755
index 0000000..1ea81c6
--- /dev/null
+++ b/example-scripts/control-server/set-composite-matrix-two-by-two.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+confdir="`dirname "$0"`/../"
+. $confdir/default-config.sh
+if [ -f $confdir/config.sh ]; then
+ . $confdir/config.sh
+fi
+
+echo set_composite_mode matrix_two_by_two | nc -q0 $MIXHOST 9999
diff --git a/example-scripts/gstreamer/source-videotestsrc-as-cam3.sh b/example-scripts/gstreamer/source-videotestsrc-as-cam3.sh
new file mode 100755
index 0000000..85787b2
--- /dev/null
+++ b/example-scripts/gstreamer/source-videotestsrc-as-cam3.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+confdir="`dirname "$0"`/../"
+. $confdir/default-config.sh
+if [ -f $confdir/config.sh ]; then
+ . $confdir/config.sh
+fi
+
+gst-launch-1.0 \
+ videotestsrc pattern=ball foreground-color=0x000000ff background-color=0x00000044 !\
+ video/x-raw,format=I420,width=$WIDTH,height=$HEIGHT,framerate=$FRAMERATE/1,pixel-aspect-ratio=1/1 ! \
+ mux. \
+ \
+ audiotestsrc freq=660 !\
+ audio/x-raw,format=S16LE,channels=2,layout=interleaved,rate=$AUDIORATE !\
+ mux. \
+ \
+ matroskamux name=mux !\
+ tcpclientsink host=$MIXHOST port=10002
diff --git a/example-scripts/gstreamer/source-videotestsrc-as-cam4.sh b/example-scripts/gstreamer/source-videotestsrc-as-cam4.sh
new file mode 100755
index 0000000..6bee2f3
--- /dev/null
+++ b/example-scripts/gstreamer/source-videotestsrc-as-cam4.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+confdir="`dirname "$0"`/../"
+. $confdir/default-config.sh
+if [ -f $confdir/config.sh ]; then
+ . $confdir/config.sh
+fi
+
+gst-launch-1.0 \
+ videotestsrc pattern=ball foreground-color=0x00ffff00 background-color=0x00444400 !\
+ video/x-raw,format=I420,width=$WIDTH,height=$HEIGHT,framerate=$FRAMERATE/1,pixel-aspect-ratio=1/1 ! \
+ mux. \
+ \
+ audiotestsrc freq=770 !\
+ audio/x-raw,format=S16LE,channels=2,layout=interleaved,rate=$AUDIORATE !\
+ mux. \
+ \
+ matroskamux name=mux !\
+ tcpclientsink host=$MIXHOST port=10003
diff --git a/example-scripts/misc/benchmark.sh b/example-scripts/misc/benchmark.sh
index c60f897..2ca879c 100755
--- a/example-scripts/misc/benchmark.sh
+++ b/example-scripts/misc/benchmark.sh
@@ -7,6 +7,8 @@ sleep 1
../control-server/set-composite-side-by-side-equal.sh >/dev/null 2>/dev/null
../gstreamer/source-videotestsrc-as-cam1.sh >/dev/null 2>/dev/null &
../gstreamer/source-videotestsrc-as-cam2.sh >/dev/null 2>/dev/null &
+../gstreamer/source-videotestsrc-as-cam3.sh >/dev/null 2>/dev/null &
+../gstreamer/source-videotestsrc-as-cam4.sh >/dev/null 2>/dev/null &
../gstreamer/source-videotestsrc-as-grabber.sh >/dev/null 2>/dev/null &
../gstreamer/source-videotestsrc-as-background-loop.sh >/dev/null 2>/dev/null &
pidstat -p $PID 1 &
diff --git a/example-scripts/misc/demo-local.sh b/example-scripts/misc/demo-local.sh
index f985fb1..95a97ce 100755
--- a/example-scripts/misc/demo-local.sh
+++ b/example-scripts/misc/demo-local.sh
@@ -12,6 +12,8 @@ echo "PID=$PID"
sleep 1
../ffmpeg/source-testvideo-as-cam1.sh >/dev/null 2>/dev/null &
../ffmpeg/source-testvideo-as-cam2.sh >/dev/null 2>/dev/null &
+../ffmpeg/source-testvideo-as-cam3.sh >/dev/null 2>/dev/null &
+../ffmpeg/source-testvideo-as-cam4.sh >/dev/null 2>/dev/null &
../ffmpeg/source-background-loop.sh &
../ffmpeg/av-record-output-ffmpeg.sh &
#../ffmpeg/stream-hd.sh &
diff --git a/example-scripts/misc/perf.sh b/example-scripts/misc/perf.sh
index c20f10b..9a03c19 100755
--- a/example-scripts/misc/perf.sh
+++ b/example-scripts/misc/perf.sh
@@ -7,6 +7,8 @@ sleep 1
../control-server/set-composite-side-by-side-equal.sh >/dev/null 2>/dev/null
../gstreamer/source-videotestsrc-as-cam1.sh >/dev/null 2>/dev/null &
../gstreamer/source-videotestsrc-as-cam2.sh >/dev/null 2>/dev/null &
+../gstreamer/source-videotestsrc-as-cam3.sh >/dev/null 2>/dev/null &
+../gstreamer/source-videotestsrc-as-cam4.sh >/dev/null 2>/dev/null &
../gstreamer/source-videotestsrc-as-grabber.sh >/dev/null 2>/dev/null &
../gstreamer/source-videotestsrc-as-background-loop.sh >/dev/null 2>/dev/null &
sudo perf top -g -p $PID
diff --git a/example-scripts/systemd-units/voctomix-cam3-source.service b/example-scripts/systemd-units/voctomix-cam3-source.service
new file mode 100644
index 0000000..3d941bc
--- /dev/null
+++ b/example-scripts/systemd-units/voctomix-cam3-source.service
@@ -0,0 +1,14 @@
+[Unit]
+Description = voctomix test-video-source cam3
+After = voctomix-voctocore.service
+Requires = voctomix-voctocore.service
+
+[Service]
+Type = simple
+ExecStart = /usr/share/doc/voctomix/example-scripts/gstreamer/source-videotestsrc-as-cam3.sh
+Restart = always
+RestartSec = 1s
+StartLimitInterval = 0
+
+[Install]
+WantedBy = voctomix-voctocore.service
diff --git a/example-scripts/systemd-units/voctomix-cam4-source.service b/example-scripts/systemd-units/voctomix-cam4-source.service
new file mode 100644
index 0000000..202c111
--- /dev/null
+++ b/example-scripts/systemd-units/voctomix-cam4-source.service
@@ -0,0 +1,14 @@
+[Unit]
+Description = voctomix test-video-source cam4
+After = voctomix-voctocore.service
+Requires = voctomix-voctocore.service
+
+[Service]
+Type = simple
+ExecStart = /usr/share/doc/voctomix/example-scripts/gstreamer/source-videotestsrc-as-cam4.sh
+Restart = always
+RestartSec = 1s
+StartLimitInterval = 0
+
+[Install]
+WantedBy = voctomix-voctocore.service
diff --git a/voctocore/default-config.ini b/voctocore/default-config.ini
index d5f69e9..3341b80 100644
--- a/voctocore/default-config.ini
+++ b/voctocore/default-config.ini
@@ -60,6 +60,17 @@ mix_out=10000
;default-a=grabber
;default-b=cam1
+[matrix-two-by-two]
+; defaults to 1% of the video width
+;gutter=12
+
+; if configured, switching to the matrix-2x2 mode will automatically select these
+; sources. if not configured, it will not change the last set sources
+;default-a=cam1
+;default-b=cam2
+;default-c=cam3
+;default-d=cam4
+
[previews]
; disable if ui & server run on the same computer and can exchange uncompressed video frames
enabled=false
diff --git a/voctocore/lib/videomix.py b/voctocore/lib/videomix.py
index 38432e8..e7c0233 100644
--- a/voctocore/lib/videomix.py
+++ b/voctocore/lib/videomix.py
@@ -12,6 +12,7 @@ class CompositeModes(Enum):
side_by_side_equal = 1
side_by_side_preview = 2
picture_in_picture = 3
+ matrix_two_by_two = 4
class PadState(object):
@@ -98,6 +99,8 @@ class VideoMix(object):
self.compositeMode = CompositeModes.fullscreen
self.sourceA = 0
self.sourceB = 1
+ self.sourceC = 2
+ self.sourceD = 3
self.recalculateMixerState()
self.applyMixerState()
@@ -129,6 +132,9 @@ class VideoMix(object):
elif self.compositeMode == CompositeModes.picture_in_picture:
self.recalculateMixerStatePictureInPicture()
+ elif self.compositeMode == CompositeModes.matrix_two_by_two:
+ self.recalculateMixerStateMatrixTwoByTwo()
+
self.log.debug('Marking Pad-State as Dirty')
self.padStateDirty = True
@@ -322,6 +328,62 @@ class VideoMix(object):
else:
pad.alpha = 0
+ def recalculateMixerStateMatrixTwoByTwo(self):
+ self.log.info('Updating Mixer-State for '
+ 'Matrix-Two-by-two-Composition')
+
+ width, height = self.getInputVideoSize()
+ self.log.debug('Video-Size parsed as %ux%u', width, height)
+
+ try:
+ gutter = Config.getint('matrix-two-by-two', 'gutter')
+ self.log.debug('Gutter configured to %u', gutter)
+ except:
+ gutter = int(width / 100)
+ self.log.debug('Gutter calculated to %u', gutter)
+
+ targetWidth = int((width - gutter) / 2)
+ #targetHeight = int(targetWidth / width * height)
+ targetHeight = int((height - gutter) / 2)
+
+ self.log.debug('Video-Size calculated to %ux%u',
+ targetWidth, targetHeight)
+
+ xa = 0
+ xb = width - targetWidth
+ y1 = 0
+ y2 = height - targetHeight
+
+ for idx, name in enumerate(self.names):
+ pad = self.padState[idx]
+ pad.reset()
+
+ pad.width = targetWidth
+ pad.height = targetHeight
+
+ if idx == self.sourceA:
+ pad.xpos = xa
+ pad.ypos = y1
+ pad.zorder = 1
+
+ elif idx == self.sourceB:
+ pad.xpos = xb
+ pad.ypos = y1
+ pad.zorder = 2
+
+ elif idx == self.sourceC:
+ pad.xpos = xa
+ pad.ypos = y2
+ pad.zorder = 3
+
+ elif idx == self.sourceD:
+ pad.xpos = xb
+ pad.ypos = y2
+ pad.zorder = 4
+
+ else:
+ pad.alpha = 0
+
def applyMixerState(self):
for idx, state in enumerate(self.padState):
# mixerpad 0 = background
@@ -346,7 +408,8 @@ class VideoMix(object):
CompositeModes.fullscreen: 'fullscreen',
CompositeModes.side_by_side_equal: 'side-by-side-equal',
CompositeModes.side_by_side_preview: 'side-by-side-preview',
- CompositeModes.picture_in_picture: 'picture-in-picture'
+ CompositeModes.picture_in_picture: 'picture-in-picture',
+ CompositeModes.matrix_two_by_two: 'matrix-two-by-two'
}
compositeModeName = self.compositeMode.name
@@ -368,6 +431,22 @@ class VideoMix(object):
except Exception as e:
pass
+ try:
+ defSource = Config.get(sectionName, 'default-c')
+ self.setVideoSourceC(self.names.index(defSource))
+ self.log.info('Changing sourceC to default of Mode %s: %s',
+ compositeModeName, defSource)
+ except Exception as e:
+ pass
+
+ try:
+ defSource = Config.get(sectionName, 'default-d')
+ self.setVideoSourceD(self.names.index(defSource))
+ self.log.info('Changing sourceD to default of Mode %s: %s',
+ compositeModeName, defSource)
+ except Exception as e:
+ pass
+
def on_handoff(self, object, buffer):
if self.padStateDirty:
self.padStateDirty = False
diff --git a/voctogui/lib/shortcuts.py b/voctogui/lib/shortcuts.py
index d5fbc91..5ad2bbf 100644
--- a/voctogui/lib/shortcuts.py
+++ b/voctogui/lib/shortcuts.py
@@ -25,7 +25,8 @@ if hasattr(Gtk, "ShortcutsWindow"):
for accel, desc in [("F1", "Select fullscreen mode"),
("F2", "Select Picture in Picture mode"),
("F3", "Select Side-by-Side Equal mode"),
- ("F4", "Select Side-by-Side Preview mode")]:
+ ("F4", "Select Side-by-Side Preview mode"),
+ ("F5", "Select Matrix Two-by-two mode")]:
short = Gtk.ShortcutsShortcut(title=desc, accelerator=accel)
short.show()
compose_group.add(short)
diff --git a/voctogui/lib/toolbar/composition.py b/voctogui/lib/toolbar/composition.py
index b55254a..3cf5797 100644
--- a/voctogui/lib/toolbar/composition.py
+++ b/voctogui/lib/toolbar/composition.py
@@ -17,7 +17,8 @@ class CompositionToolbarController(object):
'fullscreen',
'picture_in_picture',
'side_by_side_equal',
- 'side_by_side_preview'
+ 'side_by_side_preview',
+ 'matrix_two_by_two'
]
self.composite_btns = {}
diff --git a/voctogui/ui/composite-matrix-two-by-two.svg b/voctogui/ui/composite-matrix-two-by-two.svg
new file mode 100644
index 0000000..2086be2
--- /dev/null
+++ b/voctogui/ui/composite-matrix-two-by-two.svg
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg3052"
+ version="1.1"
+ inkscape:version="0.92.1 r15371"
+ sodipodi:docname="composite-matrix-two-by-two.svg">
+ <defs
+ id="defs3054" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="50.440484"
+ inkscape:cx="12.946897"
+ inkscape:cy="15.006196"
+ inkscape:document-units="px"
+ inkscape:current-layer="g3943"
+ showgrid="false"
+ inkscape:window-width="1680"
+ inkscape:window-height="1005"
+ inkscape:window-x="0"
+ inkscape:window-y="45"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata3057">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <rect
+ style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.10000000000000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3062"
+ width="30"
+ height="16.875"
+ x="1"
+ y="1027.9247" />
+ <g
+ id="g3943"
+ transform="matrix(0.9768158,0,0,0.9768158,0.70836066,24.02723)">
+ <rect
+ y="1027.8325"
+ x="0.37704754"
+ height="8.57582"
+ width="15.245902"
+ id="rect3062-3"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.25409836;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
+ <rect
+ y="1027.8325"
+ x="15.686207"
+ height="8.57582"
+ width="15.245902"
+ id="rect3062-3-8"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.25409836;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#00ff00;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.25409836;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect3690"
+ width="15.245902"
+ height="8.57582"
+ x="0.37704754"
+ y="1036.4083" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffff00;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.25409836;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect3692"
+ width="15.245902"
+ height="8.57582"
+ x="15.686207"
+ y="1036.4083" />
+ </g>
+ </g>
+</svg>
diff --git a/voctogui/ui/voctogui.ui b/voctogui/ui/voctogui.ui
index 0e0c447..53e3129 100644
--- a/voctogui/ui/voctogui.ui
+++ b/voctogui/ui/voctogui.ui
@@ -12,6 +12,11 @@
<property name="can_focus">False</property>
<property name="pixbuf">composite-fullscreen.svg</property>
</object>
+ <object class="GtkImage" id="img-composite-matrix-two-by-two">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixbuf">composite-matrix-two-by-two.svg</property>
+ </object>
<object class="GtkImage" id="img-composite-picture-in-picture">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -103,6 +108,20 @@
</packing>
</child>
<child>
+ <object class="GtkRadioToolButton" id="composite-matrix-two-by-two">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Matrix Two-by-two</property>
+ <property name="use_underline">True</property>
+ <property name="icon_widget">img-composite-matrix-two-by-two</property>
+ <property name="group">composite-fullscreen</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkSeparatorToolItem" id="s1">
<property name="visible">True</property>
<property name="can_focus">False</property>