diff options
Diffstat (limited to 'voctocore')
-rw-r--r-- | voctocore/README.md | 14 | ||||
-rw-r--r-- | voctocore/default-config.ini | 3 | ||||
-rw-r--r-- | voctocore/lib/pipeline.py | 22 | ||||
-rw-r--r-- | voctocore/lib/video/mix.py | 15 | ||||
-rw-r--r-- | voctocore/lib/video/rawoutput.py | 84 | ||||
-rw-r--r-- | voctocore/lib/video/src.py (renamed from voctocore/lib/videosrc.py) | 35 | ||||
-rw-r--r-- | voctocore/lib/videomix.py | 43 | ||||
-rw-r--r-- | voctocore/lib/videosrcmirror.py | 66 |
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') |