Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow audio and video only streams #128

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion voctocore/lib/audiomix.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ def __init__(self):
self.log = logging.getLogger('AudioMix')

self.caps = Config.get('mix', 'audiocaps')
self.names = Config.getlist('mix', 'sources')

try:
video_only = Config.getlist('mix', 'video_only')
except Exception:
video_only = []

self.names = [name for name in
Config.getlist('mix', 'sources')
if name not in video_only]

self.log.info('Configuring Mixer for %u Sources', len(self.names))

# initialize all sources to silent
Expand Down
45 changes: 35 additions & 10 deletions voctocore/lib/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,27 @@ def __init__(self, pipeline):
self.pipeline = pipeline

self.sources = Config.getlist('mix', 'sources')
self.audio_sources = []
self.video_sources = []

video_only = []
audio_only = []

try:
audio_only = Config.getlist('mix', 'audio_only')
video_only = Config.getlist('mix', 'video_only')
except Exception:
pass

for source in self.sources:
if source not in audio_only and source not in video_only:
self.audio_sources.append(source)
self.video_sources.append(source)
elif source in video_only:
self.video_sources.append(source)
elif source in audio_only:
self.audio_sources.append(source)

if Config.getboolean('stream-blanker', 'enabled'):
self.blankerSources = Config.getlist('stream-blanker', 'sources')

Expand Down Expand Up @@ -57,7 +78,11 @@ def help(self):
helplines.append('\t' + 'quit / exit')

helplines.append("\n")
helplines.append("Source-Names:")
helplines.append("Video-Source Names:")
for source in self.video_sources:
helplines.append("\t" + source)

helplines.append("Audio-Source Names:")
for source in self.sources:
helplines.append("\t" + source)

Expand All @@ -75,8 +100,8 @@ def help(self):
return OkResponse("\n".join(helplines))

def _get_video_status(self):
a = self.sources[self.pipeline.vmix.getVideoSourceA()]
b = self.sources[self.pipeline.vmix.getVideoSourceB()]
a = self.video_sources[self.pipeline.vmix.getVideoSourceA()]
b = self.video_sources[self.pipeline.vmix.getVideoSourceB()]
return [a, b]

def get_video(self):
Expand All @@ -89,7 +114,7 @@ def set_video_a(self, src_name):
"""sets the video-source A to the supplied source-name or source-id,
swapping A and B if the supplied source is currently used as
video-source B"""
src_id = self.sources.index(src_name)
src_id = self.video_sources.index(src_name)
self.pipeline.vmix.setVideoSourceA(src_id)

status = self._get_video_status()
Expand All @@ -99,7 +124,7 @@ def set_video_b(self, src_name):
"""sets the video-source B to the supplied source-name or source-id,
swapping A and B if the supplied source is currently used as
video-source A"""
src_id = self.sources.index(src_name)
src_id = self.video_sources.index(src_name)
self.pipeline.vmix.setVideoSourceB(src_id)

status = self._get_video_status()
Expand All @@ -109,7 +134,7 @@ def _get_audio_status(self):
volumes = self.pipeline.amix.getAudioVolumes()

return json.dumps({
self.sources[idx]: round(volume, 4)
self.audio_sources[idx]: round(volume, 4)
for idx, volume in enumerate(volumes)
})

Expand All @@ -120,15 +145,15 @@ def get_audio(self):

def set_audio(self, src_name):
"""sets the audio-source to the supplied source-name or source-id"""
src_id = self.sources.index(src_name)
src_id = self.audio_sources.index(src_name)
self.pipeline.amix.setAudioSource(src_id)

status = self._get_audio_status()
return NotifyResponse('audio_status', status)

def set_audio_volume(self, src_name, volume):
"""sets the volume of the supplied source-name or source-id"""
src_id = self.sources.index(src_name)
src_id = self.audio_sources.index(src_name)
volume = float(volume)
if volume < 0.0:
raise ValueError("volume must be positive")
Expand Down Expand Up @@ -163,11 +188,11 @@ def set_videos_and_composite(self, src_a_name, src_b_name,
"""sets the A- and the B-source synchronously with the composition-mode
all parametets can be set to "*" which will leave them unchanged."""
if src_a_name != '*':
src_a_id = self.sources.index(src_a_name)
src_a_id = self.video_sources.index(src_a_name)
self.pipeline.vmix.setVideoSourceA(src_a_id)

if src_b_name != '*':
src_b_id = self.sources.index(src_b_name)
src_b_id = self.video_sources.index(src_b_name)
self.pipeline.vmix.setVideoSourceB(src_b_id)

if mode_name != '*':
Expand Down
13 changes: 12 additions & 1 deletion voctocore/lib/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ def __init__(self):
self.previews = []
self.sbsources = []

audio_only = []
video_only = []
try:
audio_only = Config.getlist('mix', 'audio_only')
video_only = Config.getlist('mix', 'video_only')
except Exception:
pass

self.log.info('Creating %u AVSources: %s', len(names), names)
for idx, name in enumerate(names):
port = 10000 + idx
Expand All @@ -39,7 +47,10 @@ def __init__(self):
if Config.getboolean('mirrors', 'enabled'):
outputs.append(name + '_mirror')

source = spawn_source(name, port, outputs=outputs)
has_audio = name not in video_only
has_video = name not in audio_only
source = spawn_source(name, port, outputs=outputs,
has_audio=has_audio, has_video=has_video)
self.log.info('Creating AVSource %s as %s', name, source)
self.sources.append(source)

Expand Down
11 changes: 9 additions & 2 deletions voctocore/lib/videomix.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,15 @@ class VideoMix(object):
def __init__(self):
self.caps = Config.get('mix', 'videocaps')

self.names = Config.getlist('mix', 'sources')
self.log.info('Configuring Mixer for %u Sources', len(self.names))
try:
audio_only = Config.getlist('mix', 'audio_only')
except Exception:
audio_only = []

self.names = [name for name in
Config.getlist('mix', 'sources')
if name not in audio_only]
self.log.info('Configuring VideoMixer for %u Sources', len(self.names))

pipeline = """
compositor name=mix !
Expand Down
50 changes: 50 additions & 0 deletions voctocore/tests/test_exclusive_sources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import unittest

from lib.errors.configuration_error import ConfigurationError
from tests.helper.voctomix_test import VoctomixTest
from lib.audiomix import AudioMix
from lib.videomix import VideoMix
from lib.config import Config


# noinspection PyUnusedLocal
class ExclusiveSources(VoctomixTest):

def test_exclusive_sources_not_in_both_types(self):
Config.given("mix", "audio_only", "cam2")
Config.given("mix", "video_only", "grabber")
audiomixer = AudioMix()
videomixer = VideoMix()

self.assertListEqual(audiomixer.names, ["cam1", "cam2"])
self.assertListEqual(audiomixer.volumes, [1.0, 0.0])

self.assertListEqual(videomixer.names, ["cam1", "grabber"])

def test_exclusive_sources_dont_accept_invalid_type(self):
Config.given("mix", "audio_only", "cam2")
Config.given("mix", "video_only", "grabber")
audiomixer = AudioMix()
videomixer = VideoMix()

src_id = audiomixer.names.index("cam1")
audiomixer.setAudioSource(src_id)
# should succeed

with self.assertRaises(ValueError):
src_id = audiomixer.names.index("grabber")
audiomixer.setAudioSource(src_id)
# should fail

src_id = videomixer.names.index("cam1")
audiomixer.setAudioSource(src_id)
# should succeed

with self.assertRaises(ValueError):
src_id = videomixer.names.index("cam2")
audiomixer.setAudioSource(src_id)
# should fail


if __name__ == '__main__':
unittest.main()