Hallo,
ich experimentiere gerade, wie man von außerhalb der Desktop-Session das Ausgabegerät setzen könnte (z.B. um das über das Playbook oder später auch über das Webfrontend zu machen) - damit das unabhängig von einem laufenden VDR möglich ist (das pulsecontrol-Plugin von mini73 bietet da ja schon ein SVDRP-Interface) wäre mein Ansatz ist ein kleines Python-Skript zu nutzen, das in der Session läuft, aber auf dem System-Bus über DBus erreichbar ist - damit DBus das erlaubt, benötigt man folgende Policy:
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only user vdr can own the dbus-service -->
<policy user="vdr">
<allow own="org.yavdr.PulseDBusCtl"/>
</policy>
<!-- allow everyone to call to call the dbus methods -->
<policy context="default">
<allow send_destination="org.yavdr.PulseDBusCtl"/>
<allow receive_sender="org.yavdr.PulseDBusCtl"/>
</policy>
</busconfig>
Alles anzeigen
Für das Skript benötigt werden das Paket python3-dasbus und python-pulse-control (das kann man der Einfachheit halber zum Testen in einem venv installieren, später würde ich ein Paket dafür erstellen):
# Abhängigkeiten installieren
sudo apt install python3-dasbus python3-venv
# eine Shell in der User Session öffnen:
sudo tmux -S /tmp/tmux-666/default
# in der User-Session
python -m venv --system-site-packages ~/.pulsectl-venv
. ~/.pulsectl-venv/bin/activate
pip install -U pip pulsectl
Das Skript sieht so aus (der Pfad kann grundsätzlich beliebig gewählt werden):
#!/usr/bin/env python3
from dasbus.connection import SystemMessageBus
from dasbus.loop import EventLoop
import pulsectl
pulse = pulsectl.Pulse('pulse_dbus_ctl')
loop = EventLoop()
bus = SystemMessageBus()
class PulseDBusCtl(object):
__dbus_xml__ = """
<node>
<interface name="org.yavdr.PulseDBusCtl">
<method name="ListSinks">
<!--
Returns an array of
structs containing data for each sink:
name: string
description: string
index: int32
muted: bool
number of channels: int32
volume_values: array of doubles
port_active: string one of ["yes", "no", "unknown"]
-->
<arg direction="out" name="output_sinks" type="a(ssibiads)" />
</method>
<method name="SetDefaultSink">
<!--
set the default sink by a given sink name, e.g.
'alsa_output.pci-0000_01_00.1.hdmi-stereo'
-->
<arg direction="in" name="sink_name" type="s" />
<arg direction="out" name="success" type="b" />
</method>
</interface>
</node>
"""
def ListSinks(self):
return [
(
s.name,
s.description,
s.index,
s.mute,
s.channel_count,
s.volume.values,
s.port_active.available_state._value
) for s in pulse.sink_list()
]
def SetDefaultSink(self, sink_name: str) -> bool:
try:
target_sink = pulse.get_sink_by_name(sink_name)
except:
print("could not get target sink")
return False
try:
pulse.sink_default_set(target_sink)
except Exception as e:
print(e)
return False
# move all streams to the new default sink
for stream in pulse.sink_input_list():
pulse.sink_input_move(stream.index, target_sink.index)
return True
bus.publish_object("/org/yavdr/PulseDBusCtl", PulseDBusCtl())
bus.register_service("org.yavdr.PulseDBusCtl")
try:
loop.run()
except KeyboardInterrupt:
loop.quit()
pulse.disconnect()
Alles anzeigen
Jetzt sollte man es aus der User-Session heraus so aufrufen können:
~/.pulsectl-venv/bin/python ~/bin/pulse_dbus_ctl.py
Was mich jetzt interessiert, sind in einem zweiten Terminal die Ausgaben von dbus-send --system --type=method_call --dest=org.yavdr.PulseDBusCtl --print-reply /org/yavdr/PulseDBusCtl org.yavdr.PulseDBusCtl.ListSinks
Und ob das Wechseln des Ausgabegeräts inkl. bewegen der Input-Sinks auf das neue Ausgabegerät klappt - z.B. hat mein Test-System einen optischen SPDIF-Ausgang und einen HDMI-Ausgang - der erste String im struct ist der Name des Ausgabegeräts, den man nutzen kann, um die Ausgabe umzustellen:
$ dbus-send --system --type=method_call --dest=org.yavdr.PulseDBusCtl --print-reply /org/yavdr/PulseDBusCtl org.yavdr.PulseDBusCtl.ListSinks
method return time=1640348395.894321 sender=:1.143 -> destination=:1.144 serial=4 reply_serial=2
array [
struct {
string "alsa_output.pci-0000_00_1b.0.iec958-stereo"
string "Eingebautes Tongerät Digital Stereo (IEC958)"
int32 1
boolean false
int32 2
array [
double 1
double 1
]
string "unknown"
}
struct {
string "alsa_output.pci-0000_01_00.1.hdmi-stereo"
string "GP108 High Definition Audio Controller Digital Stereo (HDMI)"
int32 2
boolean false
int32 2
array [
double 1
double 1
]
string "yes"
}
]
Alles anzeigen
Mit dem Device-Namen kann ich programmatisch das Ausgabegerät wechseln:
# Wechsel auf HDMI-Ausgabe
dbus-send --system --type=method_call --dest=org.yavdr.PulseDBusCtl --print-reply /org/yavdr/PulseDBusCtl org.yavdr.PulseDBusCtl.SetDefaultSink string:"alsa_output.pci-0000_01_00.1.hdmi-stereo"
# Wechsel auf S/PDIF
dbus-send --system --type=method_call --dest=org.yavdr.PulseDBusCtl --print-reply /org/yavdr/PulseDBusCtl org.yavdr.PulseDBusCtl.SetDefaultSink string:"alsa_output.pci-0000_00_1b.0.iec958-stereo"
Wie sieht das bei euch aus? Mich würde da vor allem interessieren, wie das mit Receivern funktioniert, die mehr als Stereo-Ausgabeprofile unterstützen.