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

Fix audio only sources #350

Merged
merged 3 commits into from
Nov 9, 2024
Merged
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
2 changes: 1 addition & 1 deletion vocto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
os.environ['GST_DEBUG_DUMP_DOT_DIR'] = os.getcwd()

def kind_has_audio(source):
return source in ["decklink", "tcp", "test", "pa"]
return source in ["decklink", "tcp", "test", "pa", "alsa"]

def kind_has_video(source):
return source in ["decklink", "tcp", "test", "v4l2", "img", "file", "background", "RPICam"]
19 changes: 10 additions & 9 deletions voctocore/lib/avrawoutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ def __init__(self, source, port, use_audio_mix=False, audio_blinded=False):
""".format(source=self.source)

# video pipeline
self.bin += """
video-{source}.
! {vcaps}
! queue
max-size-time=3000000000
name=queue-mux-video-{source}
! mux-{source}.
""".format(source=self.source,
vcaps=Config.getVideoCaps())
if source in Config.getVideoSources(internal=True):
self.bin += """
video-{source}.
! {vcaps}
! queue
max-size-time=3000000000
name=queue-mux-video-{source}
! mux-{source}.
""".format(source=self.source,
vcaps=Config.getVideoCaps())

# audio pipeline
if use_audio_mix or source in Config.getAudioSources(internal=True):
Expand Down
97 changes: 97 additions & 0 deletions voctogui/lib/audioonlydisplay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import logging
import sys

from gi.repository import Gst, Gdk

from lib.args import Args
from lib.config import Config
from lib.clock import Clock

from vocto.port import Port
from vocto.debug import gst_generate_dot
from vocto.pretty import pretty

class AudioOnlyDisplay(object):
"""Displays a Voctomix-AudioOnly-Stream into a GtkWidget"""

def __init__(self, audio_display, port, name, play_audio=False):
self.log = logging.getLogger('AudioOnlyDisplay:%s' % name)
self.name = name
self.level_callback = None if audio_display is None else audio_display.callback

# Setup Server-Connection, Demuxing and Decoding
pipe = """
tcpclientsrc
name=tcpsrc-{name}
host={host}
port={port}
blocksize=1048576
! matroskademux
name=demux-{name}
""".format(name=name,
host=Config.getHost(),
port=port)

# add an Audio-Path through a level-Element
pipe += """
demux-{name}.
! queue
name=queue-audio-{name}
! level
name=lvl
interval=50000000
! audioconvert
"""

# If Playback is requested, push fo pulseaudio
if play_audio:
pipe += """ ! pulsesink
name=audiosink-{name}
"""
else:
pipe += """ ! fakesink
"""
pipe = pipe.format(name=name,
acaps=Config.getAudioCaps(),
port=port,
)

self.log.info("Creating Display-Pipeline:\n%s", pretty(pipe))
try:
# launch gstreamer pipeline
self.pipeline = Gst.parse_launch(pipe)
self.log.info("pipeline launched successfuly")
except:
self.log.error("Can not launch pipeline")
sys.exit(-1)

if Args.dot:
self.log.debug("Generating DOT image of audioonlydisplay pipeline")
gst_generate_dot(self.pipeline, "gui.audioonlydisplay.{}".format(name))

self.pipeline.use_clock(Clock)

bus = self.pipeline.get_bus()
bus.add_signal_watch()

bus.connect('message::error', self.on_error)
bus.connect("message::element", self.on_level)

self.log.info("Launching Display-Pipeline")
self.pipeline.set_state(Gst.State.PLAYING)

def on_error(self, bus, message):
(error, debug) = message.parse_error()
self.log.error(
"GStreamer pipeline element '%s' signaled an error #%u: %s" % (message.src.name, error.code, error.message))

def mute(self, mute):
self.pipeline.get_by_name("audiosink-{name}".format(name=self.name)).set_property(
"volume", 1 if mute else 0)
Copy link
Contributor

@Kunsi Kunsi Nov 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, this is inverted (but also inverted in videodisplay.py, so let's keep this here as well).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tbh, this is just a quick workaround - a better long-term solution would probably be for videodisplay to handle optional audio or optional video itself.


def on_level(self, bus, msg):
if self.level_callback and msg.src.name == 'lvl':
rms = msg.get_structure().get_value('rms')
peak = msg.get_structure().get_value('peak')
decay = msg.get_structure().get_value('decay')
self.level_callback(rms, peak, decay)
3 changes: 3 additions & 0 deletions voctogui/lib/videopreviews.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from gi.repository import Gtk, Gdk, GObject
from lib.videodisplay import VideoDisplay
from lib.audioonlydisplay import AudioOnlyDisplay
from lib.audiodisplay import AudioDisplay
import lib.connection as Connection

Expand Down Expand Up @@ -72,3 +73,5 @@ def addPreview(self, uibuilder, source, port, has_volume=True):
name=source.upper(),
has_audio=has_audio,
)
elif has_audio and Config.getAudioStreams().get_source_streams(source):
player = AudioOnlyDisplay(mix_audio_display, port=port, name=source.upper())