diff --git a/configs/gstsource/README.md b/configs/gstsource/README.md new file mode 100644 index 00000000..a8b6c5b5 --- /dev/null +++ b/configs/gstsource/README.md @@ -0,0 +1 @@ +sample core.ini used to test gstsource.py diff --git a/configs/gstsource/gst_4s.ini b/configs/gstsource/gst_4s.ini new file mode 100644 index 00000000..791671d1 --- /dev/null +++ b/configs/gstsource/gst_4s.ini @@ -0,0 +1,35 @@ +[mix] +sources = gst1,gst2,gst3,gst4 + +[source.gst1] +kind=gst +audio_source=audiotestsrc freq=550 ! audio/x-raw,format=S16LE,channels=2 +audio_debug=True +video_source=videotestsrc pattern=ball motion=hsweep animation-mode=wall-time flip=true is-live=true +video_debug=True +audio.original=0+1 + +[source.gst2] +kind=gst +audio_source=audiotestsrc freq=1200 +audio_debug=True +video_source=videotestsrc pattern=red ! textoverlay text=hello +video_debug=True + +[source.gst3] +kind=gst +audio_debug=True +video_source=videotestsrc pattern=blue ! textoverlay text='good bye' +video_debug=True + +[source.gst4] +kind=gst +audio_debug=True +video_source=multifilesrc location=data/images/voc2bg.png caps=image/png,width=1920,height=1080 loop=true ! queue ! pngdec ! videoscale ! videoconvert ! videorate ! queue +video_debug=True + +[composites] +FULL.alpha-b = 0 + +[transitions] +FADE = 750, FULL / FULL diff --git a/configs/gstsource/gst_minimal.ini b/configs/gstsource/gst_minimal.ini new file mode 100644 index 00000000..6d6f55e3 --- /dev/null +++ b/configs/gstsource/gst_minimal.ini @@ -0,0 +1,13 @@ +[mix] +# core errors if there is only one source +sources = Gst,Test + +[source.Gst] +kind=gst +audio.original=0+1 + +[composites] +FULL.alpha-b = 0 + +[transitions] +FADE = 750, FULL / FULL diff --git a/configs/gstsource/gst_vivid.ini b/configs/gstsource/gst_vivid.ini new file mode 100644 index 00000000..b09dd489 --- /dev/null +++ b/configs/gstsource/gst_vivid.ini @@ -0,0 +1,25 @@ +[mix] +sources = vivid,gsttst + +[source.vivid] +kind=gst +audio_debug=True + +# modprobe vivid +# v4l2-ctl --device /dev/v4l/by-path/platform-vivid.0-video-index0 --set-ctrl show_square=1 +video_source=v4l2src device=/dev/v4l/by-path/platform-vivid.0-video-index0 +video_debug=True + +audio.original=0+1 + +[source.gsttst] +kind=gst +audio_debug=True +video_source=videotestsrc pattern=ball motion=hsweep animation-mode=wall-time flip=true is-live=true +video_debug=True + +[composites] +FULL.alpha-b = 0 + +[transitions] +FADE = 750, FULL / FULL diff --git a/vocto/__init__.py b/vocto/__init__.py index 52a5e77e..1d0b193a 100644 --- a/vocto/__init__.py +++ b/vocto/__init__.py @@ -9,7 +9,7 @@ os.environ['GST_DEBUG_DUMP_DOT_DIR'] = os.getcwd() def kind_has_audio(source: str) -> bool: - return source in ["aja", "decklink", "tcp", "test", "pa", "alsa"] + return source in ["aja", "decklink", "tcp", "test", "pa", "alsa", "gst"] def kind_has_video(source: str) -> bool: - return source in ["aja", "decklink", "tcp", "test", "v4l2", "img", "file", "background", "RPICam"] + return source in ["aja", "decklink", "tcp", "test", "v4l2", "img", "file", "background", "RPICam", "gst"] diff --git a/vocto/config.py b/vocto/config.py index 9b2389d6..77d24558 100644 --- a/vocto/config.py +++ b/vocto/config.py @@ -127,7 +127,7 @@ def getDeckLinkVideoMode(self, source) -> str: def getDeckLinkVideoFormat(self, source) -> str: return self.get('source.{}'.format(source), 'video_format', fallback='auto') - + def getAJADeviceIdentifier(self, source) -> str: return self.get(f'source.{source}', 'device', fallback='') @@ -524,3 +524,24 @@ def source_has_audio(source: str) -> bool: if internal: sources.extend(self._getInternalSources()) return list(filter(source_has_audio, sources)) + + def getGstAudioPipe(self, source) -> str: + return self.get(f'source.{source}', + 'audio_source', + fallback='audiotestsrc wave=ticks freq=330') + + def getGstAudioDebug(self, source) -> bool: + return self.get(f'source.{source}', + 'audio_debug', + fallback=False) + + def getGstVideoPipe(self, source) -> str: + return self.get(f'source.{source}', + 'video_source', + fallback='videotestsrc pattern=ball motion=hsweep animation-mode=wall-time') + + def getGstVideoDebug(self, source) -> bool: + return self.get(f'source.{source}', + 'video_debug', + fallback=False) + diff --git a/voctocore/lib/sources/__init__.py b/voctocore/lib/sources/__init__.py index 74cbd7f6..515f0aa4 100644 --- a/voctocore/lib/sources/__init__.py +++ b/voctocore/lib/sources/__init__.py @@ -21,6 +21,8 @@ def spawn_source(name: str, port: Optional[int], has_audio: bool=True, has_video from voctocore.lib.sources.pulseaudiosource import PulseAudioSource from voctocore.lib.sources.alsaaudiosource import AlsaAudioSource + from voctocore.lib.sources.gstsource import GstAVSource + kind = Config.getSourceKind(name) if kind == 'img': @@ -41,6 +43,8 @@ def spawn_source(name: str, port: Optional[int], has_audio: bool=True, has_video sources[name] = PulseAudioSource(name) elif kind == 'alsa': sources[name] = AlsaAudioSource(name) + elif kind == 'gst': + sources[name] = GstAVSource(name) else: if kind != 'test': log.warning( diff --git a/voctocore/lib/sources/gstsource.py b/voctocore/lib/sources/gstsource.py new file mode 100644 index 00000000..7ff91321 --- /dev/null +++ b/voctocore/lib/sources/gstsource.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +import logging + +from gi.repository import Gst + +from voctocore.lib.config import Config +from voctocore.lib.sources.avsource import AVSource + + +class GstAVSource(AVSource): + def __init__(self, name, has_audio=True, has_video=True, + force_num_streams=None): + super().__init__('GstAVSource', name, has_audio, has_video, + force_num_streams) + + self.name = name + self.audio_source = Config.getGstAudioPipe(name) + self.audio_debug = Config.getGstAudioDebug(name) + self.video_source = Config.getGstVideoPipe(name) + self.video_debug = Config.getGstVideoDebug(name) + self.build_pipeline() + + def num_connections(self): + return 1 + + def port(self): + if self.internal_audio_channels(): + audio_hint=self.audio_source.split()[0] + else: + audio_hint="(None)" + if self.has_video: + video_hint=self.video_source.split()[0] + else: + video_hint="(None)" + + return f"(AV:{audio_hint}+{video_hint})" + + def __str__(self): + return f'GSTSource[{self.name}] ({self.port()})' + + def build_audioport(self): + return self.audio_source + + def build_videoport(self): + vpipe=self.video_source + + texts=[] + if self.audio_debug: + texts.append(self.audio_source) + if self.video_debug: + texts.append(self.video_source) + if texts: + text = "\n".join(texts) + vpipe+=f' ! clockoverlay text="{self.name=}\n{text}\n" halignment=left line-alignment=left' + + return vpipe diff --git a/voctocore/lib/sources/v4l2source.py b/voctocore/lib/sources/v4l2source.py index 9fd9a614..4dae2610 100644 --- a/voctocore/lib/sources/v4l2source.py +++ b/voctocore/lib/sources/v4l2source.py @@ -83,6 +83,10 @@ def build_source(self): ! videoconvert ! videoscale ! videorate + """ + pipe = f'fallbacksrc source="{pipe}"' + + pipe += """\ name=vout-{name} """.format(name=self.name)