aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--voctocore/README.md14
-rw-r--r--voctocore/default-config.ini3
-rw-r--r--voctocore/lib/pipeline.py22
-rw-r--r--voctocore/lib/video/mix.py15
-rw-r--r--voctocore/lib/video/rawoutput.py84
-rw-r--r--voctocore/lib/video/src.py (renamed from voctocore/lib/videosrc.py)35
-rw-r--r--voctocore/lib/videomix.py43
-rw-r--r--voctocore/lib/videosrcmirror.py66
8 files changed, 146 insertions, 136 deletions
diff --git a/voctocore/README.md b/voctocore/README.md
index df6abf8..40bb793 100644
--- a/voctocore/README.md
+++ b/voctocore/README.md
@@ -1,11 +1,13 @@
````
- /-> Encoder -> PreviewPort 12000
- /-> VideoMix -> OutputPort 11000
-10000… VideoSrc --> MirrorPort 13000…
- \-> Encoder -> PreviewPort 14000…
+ /-> Encoder -> PreviewPort 12000
+ /-> VideoMix --> OutputPort 11000
+ / \-> StreamBlanker -> StreamOutputPort 11001
+10000… VideoSrc --> MirrorPort 13000…
+ \-> Encoder -> PreviewPort 14000…
- /-> Encoder -> PreviewPort 22000
- /-> AudioMix --> OutputPort 21000
+ /-> Encoder -> PreviewPort 22000
+ /-> AudioMix --> OutputPort 21000
+ / \-> StreamBlanker -> StreamOutputPort 21001
20000… AudioSrc --> MirrorPort 23000…
\-> Encoder -> PreviewPort 24000…
````
diff --git a/voctocore/default-config.ini b/voctocore/default-config.ini
index 51f9c83..ccf80b5 100644
--- a/voctocore/default-config.ini
+++ b/voctocore/default-config.ini
@@ -2,6 +2,9 @@
videocaps=video/x-raw,format=I420,width=1280,height=720,framerate=25/1,pixel-aspect-ratio=1/1
audiocaps=audio/x-raw,format=S16LE,channels=2,layout=interleaved,rate=48000
+; disable if ui & server run on the same computer and excahnge uncompressed video frames
+encode_previews=true
+
[sources]
; tcp-ports will be 10000,10001
; video=cam1,cam2,grabber
diff --git a/voctocore/lib/pipeline.py b/voctocore/lib/pipeline.py
index 6c207fe..5687573 100644
--- a/voctocore/lib/pipeline.py
+++ b/voctocore/lib/pipeline.py
@@ -7,8 +7,9 @@ from lib.controlserver import controlServerEntrypoint
# import library components
from lib.config import Config
-from lib.videosrc import VideoSrc
-from lib.videosrcmirror import VideoSrcMirror
+from lib.video.src import VideoSrc
+from lib.video.rawoutput import VideoRawOutput
+from lib.video.mix import VideoMix
class Pipeline(object):
"""mixing, streaming and encoding pipeline constuction and control"""
@@ -16,15 +17,9 @@ class Pipeline(object):
vsources = []
vmirrors = []
+ vpreviews = []
def __init__(self):
- # self.log.debug('Creating A/V-Mixer')
- # self.videomixer = VideoMix()
- # self.add(self.videomixer)
-
- # self.audiomixer = AudioMix()
- # self.add(self.audiomixer)
-
caps = Config.get('mix', 'videocaps')
self.log.info('Video-Caps configured to: %s', caps)
@@ -39,5 +34,12 @@ class Pipeline(object):
port = 13000 + idx
self.log.info('Creating Mirror-Output for Video-Source %s at tcp-port %u', name, port)
- mirror = VideoSrcMirror(name, port, caps)
+ mirror = VideoRawOutput('video_%s_mirror' % name, port, caps)
self.vmirrors.append(mirror)
+
+ # self.log.debug('Creating Video-Mixer')
+ # self.videomixer = VideoMix()
+
+ # port = 11000
+ # self.log.debug('Creating Video-Mixer-Output at tcp-port %u', port)
+ # mirror = VideoRawOutput('video_mix', port, caps)
diff --git a/voctocore/lib/video/mix.py b/voctocore/lib/video/mix.py
new file mode 100644
index 0000000..2d73d7b
--- /dev/null
+++ b/voctocore/lib/video/mix.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python3
+import logging
+from gi.repository import Gst
+
+from lib.config import Config
+
+class VideoMix(object):
+ log = logging.getLogger('VideoMix')
+
+ def __init__(self):
+ caps = Config.get('mix', 'videocaps')
+
+ names = Config.getlist('sources', 'video')
+ self.log.info('Configuring Mixer for %u Sources', len(names))
+ #for idx, name in enumerate():
diff --git a/voctocore/lib/video/rawoutput.py b/voctocore/lib/video/rawoutput.py
new file mode 100644
index 0000000..ecdb1ba
--- /dev/null
+++ b/voctocore/lib/video/rawoutput.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python3
+import logging, socket
+from gi.repository import GObject, Gst
+
+from lib.config import Config
+
+class VideoRawOutput(object):
+ log = logging.getLogger('VideoRawOutput')
+
+ name = None
+ port = None
+ caps = None
+
+ boundSocket = None
+
+ receiverPipelines = []
+ currentConnections = []
+
+ def __init__(self, channel, port, caps):
+ self.log = logging.getLogger('VideoRawOutput['+channel+']')
+
+ self.channel = channel
+ self.port = port
+ self.caps = caps
+
+ self.log.debug('Binding to Mirror-Socket on [::]:%u', port)
+ self.boundSocket = socket.socket(socket.AF_INET6)
+ self.boundSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.boundSocket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
+ self.boundSocket.bind(('::', port))
+ self.boundSocket.listen(1)
+
+ self.log.debug('Setting GObject io-watch on Socket')
+ GObject.io_add_watch(self.boundSocket, GObject.IO_IN, self.on_connect)
+
+ def on_connect(self, sock, *args):
+ conn, addr = sock.accept()
+ self.log.info("Incomming Connection from %s", addr)
+
+ pipeline = """
+ intervideosrc channel={channel} !
+ {caps} !
+ textoverlay text={channel} halignment=left valignment=top ypad=125 !
+ gdppay !
+ fdsink fd={fd}
+ """.format(
+ fd=conn.fileno(),
+ channel=self.channel,
+ caps=self.caps
+ )
+ self.log.debug('Launching Pipeline:\n%s', pipeline)
+ receiverPipeline = Gst.parse_launch(pipeline)
+
+ def on_eos(bus, message):
+ self.log.info('Received End-of-Stream-Signal on Source-Receiver-Pipeline')
+ self.disconnect(receiverPipeline, conn)
+
+ def on_error(bus, message):
+ self.log.info('Received Error-Signal on Source-Receiver-Pipeline')
+
+ (error, debug) = message.parse_error()
+ self.log.debug('Error-Message %s\n%s', error.message, debug)
+
+ self.disconnect(receiverPipeline, conn)
+
+ self.log.debug('Binding End-of-Stream-Signal on Pipeline')
+ receiverPipeline.bus.add_signal_watch()
+ receiverPipeline.bus.connect("message::eos", on_eos)
+ receiverPipeline.bus.connect("message::error", on_error)
+
+ receiverPipeline.set_state(Gst.State.PLAYING)
+
+ self.receiverPipelines.append(receiverPipeline)
+ self.currentConnections.append(conn)
+
+ self.log.info('Now %u Receiver connected', len(self.currentConnections))
+
+ return True
+
+ def disconnect(self, receiverPipeline, currentConnection):
+ receiverPipeline.set_state(Gst.State.NULL)
+ self.receiverPipelines.remove(receiverPipeline)
+ self.currentConnections.remove(currentConnection)
+ self.log.info('Disconnected Receiver, now %u Receiver connected', len(self.currentConnections))
diff --git a/voctocore/lib/videosrc.py b/voctocore/lib/video/src.py
index 10b63fb..dbbe12d 100644
--- a/voctocore/lib/videosrc.py
+++ b/voctocore/lib/video/src.py
@@ -25,16 +25,16 @@ class VideoSrc(object):
self.caps = caps
pipeline = """
- intervideosrc channel={name}_in !
+ intervideosrc channel=video_{name}_in !
{caps} !
timeoverlay halignment=left valignment=top !
- textoverlay text={name}_in halignment=left valignment=top ypad=75 !
+ textoverlay text=video_{name}_in halignment=left valignment=top ypad=75 !
queue !
tee name=tee
- tee. ! queue ! intervideosink channel={name}_mirror
- tee. ! queue ! intervideosink channel={name}_preview
- tee. ! queue ! intervideosink channel={name}_mixer
+ tee. ! queue ! intervideosink channel=video_{name}_mirror
+ tee. ! queue ! intervideosink channel=video_{name}_preview
+ tee. ! queue ! intervideosink channel=video_{name}_mixer
""".format(
name=self.name,
caps=self.caps
@@ -56,17 +56,18 @@ class VideoSrc(object):
def on_connect(self, sock, *args):
conn, addr = sock.accept()
- self.log.info("incomming connection from %s", addr)
+ self.log.info("Incomming Connection from %s", addr)
if self.currentConnection is not None:
- self.log.warn("another source is already connected")
+ self.log.warn("Another Source is already connected")
return True
pipeline = """
fdsrc fd={fd} !
gdpdepay !
{caps} !
- intervideosink channel={name}_in
+ textoverlay text=video_{name}_fd halignment=left valignment=top ypad=175 !
+ intervideosink channel=video_{name}_in
""".format(
fd=conn.fileno(),
name=self.name,
@@ -77,16 +78,28 @@ class VideoSrc(object):
self.log.debug('Binding End-of-Stream-Signal on Source-Receiver-Pipeline')
self.receiverPipeline.bus.add_signal_watch()
- self.receiverPipeline.bus.connect("message::eos", self.on_disconnect)
+ self.receiverPipeline.bus.connect("message::eos", self.on_eos)
+ self.receiverPipeline.bus.connect("message::error", self.on_error)
self.receiverPipeline.set_state(Gst.State.PLAYING)
self.currentConnection = conn
return True
- def on_disconnect(self, bus, message):
+ def on_eos(self, bus, message):
self.log.info('Received End-of-Stream-Signal on Source-Receiver-Pipeline')
+ if self.currentConnection is not None:
+ self.disconnect()
+
+ def on_error(self, bus, message):
+ self.log.info('Received Error-Signal on Source-Receiver-Pipeline')
+ (code, debug) = message.parse_error()
+ self.log.debug('Error-Details: #%u: %s', code, debug)
+
+ if self.currentConnection is not None:
+ self.disconnect()
+
+ def disconnect(self):
self.receiverPipeline.set_state(Gst.State.NULL)
self.receiverPipeline = None
-
self.currentConnection = None
diff --git a/voctocore/lib/videomix.py b/voctocore/lib/videomix.py
deleted file mode 100644
index 9207723..0000000
--- a/voctocore/lib/videomix.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/python3
-import time, logging
-from gi.repository import GLib, Gst
-
-from lib.config import Config
-
-class VideoMix(object):
- log = logging.getLogger('VideoMix')
- mixerpads = []
-
- 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'))
- )
-
- def request_mixer_pad(self):
- mixerpad = self.mixer.get_request_pad('sink_%u')
- self.mixerpads.append(mixerpad)
- mixerpad.set_property('alpha', 1)
-
- self.log.info('requested mixerpad %u (named %s)', len(self.mixerpads) - 1, mixerpad.get_name())
- ghostpad = Gst.GhostPad.new(mixerpad.get_name(), mixerpad)
- self.add_pad(ghostpad)
- return ghostpad
-
- def set_active(self, target):
- self.log.info('setting videosource %u active, disabling other', target)
- for idx, mixerpad in enumerate(self.mixerpads):
- mixerpad.set_property('alpha', int(target == idx))
diff --git a/voctocore/lib/videosrcmirror.py b/voctocore/lib/videosrcmirror.py
deleted file mode 100644
index 8fb911d..0000000
--- a/voctocore/lib/videosrcmirror.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/python3
-import logging, socket
-from gi.repository import GObject, Gst
-
-from lib.config import Config
-
-class VideoSrcMirror(object):
- log = logging.getLogger('VideoSrcMirror')
-
- name = None
- port = None
- caps = None
-
- boundSocket = None
-
- receiverPipelines = []
- currentConnections = []
-
- def __init__(self, name, port, caps):
- self.log = logging.getLogger('VideoSrcMirror['+name+']')
-
- self.name = name
- self.port = port
- self.caps = caps
-
- self.log.debug('Binding to Mirror-Socket on [::]:%u', port)
- self.boundSocket = socket.socket(socket.AF_INET6)
- self.boundSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.boundSocket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
- self.boundSocket.bind(('::', port))
- self.boundSocket.listen(1)
-
- self.log.debug('Setting GObject io-watch on Socket')
- GObject.io_add_watch(self.boundSocket, GObject.IO_IN, self.on_connect)
-
- def on_connect(self, sock, *args):
- conn, addr = sock.accept()
- self.log.info("incomming connection from %s", addr)
-
- pipeline = """
- intervideosrc channel={name}_mirror !
- {caps} !
- textoverlay text={name}_mirror halignment=left valignment=top ypad=125 !
- gdppay !
- fdsink fd={fd}
- """.format(
- fd=conn.fileno(),
- name=self.name,
- caps=self.caps
- )
- self.log.debug('Launching Mirror-Receiver-Pipeline:\n%s', pipeline)
- receiverPipeline = Gst.parse_launch(pipeline)
-
- self.log.debug('Binding End-of-Stream-Signal on Source-Receiver-Pipeline')
- receiverPipeline.bus.add_signal_watch()
- receiverPipeline.bus.connect("message::eos", self.on_disconnect)
-
- receiverPipeline.set_state(Gst.State.PLAYING)
-
- self.receiverPipelines.append(receiverPipeline)
- self.currentConnections.append(conn)
-
- return True
-
- def on_disconnect(self, bus, message):
- self.log.info('Received End-of-Stream-Signal on Source-Receiver-Pipeline')