mk-fg / python-pulse-control

Python high-level interface and ctypes-based bindings for PulseAudio (libpulse)
https://pypi.org/project/pulsectl/
MIT License
170 stars 36 forks source link

Cannot load modules with `module_load` #65

Closed jonathanjsimon closed 2 years ago

jonathanjsimon commented 2 years ago

Should I expect module_load to work?

I'm just testing in a test script right now but when I try to load a module, I get a large return value but there is no effect in pulseaudio. I'm on a Debian 10 system where pulse is running in system mode. Yes, I know what the pulse maintainers think of system mode. Yes, my use case is in their list of reasons to do it. pulsectl is version 21.5.18 and python3 is version 3.7.3.

#!/usr/bin/env python3

import pulsectl
pulse = pulsectl.Pulse("myclient")

print("sinks:")
for sink in pulse.sink_list():
    print (sink)

print("create unnamed null sink")
print(pulse.module_load("module-null-sink"))

print("sinks:")
for sink in pulse.sink_list():
    print (sink)

print("create named null sink")
print(pulse.module_load("module-null-sink", args='sink_name="default.2.null"'))

print("sinks:")
for sink in pulse.sink_list():
    print (sink)

output:

sinks:
description='Built-in Audio Analog Stereo', index=0, mute=1, name='alsa_output.pci-0000_00_1b.0.analog-stereo', channels=2, volumes=[9% 9%]
description='Null Output', index=1, mute=0, name='default.null', channels=2, volumes=[100% 100%]
create unnamed null sink
4294967295
sinks:
description='Built-in Audio Analog Stereo', index=0, mute=1, name='alsa_output.pci-0000_00_1b.0.analog-stereo', channels=2, volumes=[9% 9%]
description='Null Output', index=1, mute=0, name='default.null', channels=2, volumes=[100% 100%]
create named null sink
4294967295
sinks:
description='Built-in Audio Analog Stereo', index=0, mute=1, name='alsa_output.pci-0000_00_1b.0.analog-stereo', channels=2, volumes=[9% 9%]
description='Null Output', index=1, mute=0, name='default.null', channels=2, volumes=[100% 100%]

Thanks!

mk-fg commented 2 years ago

Yeah, afaict this works in general: https://github.com/mk-fg/python-pulse-control/blob/master/pulsectl/tests/test_with_dummy_instance.py#L436-L442

And running same exact script here, I get:

sinks:
description='Built-in Audio Analog Stereo', index=0, mute=0, name='alsa_output.pci-0000_00_14.2.analog-stereo', channels=2, volumes=[71% 71%]
description='Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)', index=17, mute=0, name='alsa_output.pci-0000_04_00.1.hdmi-stereo', channels=2, volumes=[71% 71%]
create unnamed null sink
30
sinks:
description='Built-in Audio Analog Stereo', index=0, mute=0, name='alsa_output.pci-0000_00_14.2.analog-stereo', channels=2, volumes=[71% 71%]
description='Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)', index=17, mute=0, name='alsa_output.pci-0000_04_00.1.hdmi-stereo', channels=2, volumes=[71% 71%]
description='Null Output', index=20, mute=0, name='null', channels=2, volumes=[100% 100%]
create named null sink
31
sinks:
description='Built-in Audio Analog Stereo', index=0, mute=0, name='alsa_output.pci-0000_00_14.2.analog-stereo', channels=2, volumes=[71% 71%]
description='Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)', index=17, mute=0, name='alsa_output.pci-0000_04_00.1.hdmi-stereo', channels=2, volumes=[71% 71%]
description='Null Output', index=20, mute=0, name='null', channels=2, volumes=[100% 100%]
description='Null Output', index=21, mute=0, name='default.2.null', channels=2, volumes=[100% 100%]

In your case module_load seem to be retuning 4294967295 (INT_MAX), and that probabably should be handled and raised as exception, but underlying issue is pulse refusing to load the module for some reason. You can run the daemon with -v to get a lot of logging on what it does and what's the actual error, if it's not already logged (probably should be), as module_load probably returns INT_MAX without any additional explaination.

mk-fg commented 2 years ago

Should be fixed in 8d806c8 / 21.9.0 - should work roughly like this now:

def test_module_funcs(self):
  with pulsectl.Pulse('t', server=self.sock_unix) as pulse:
    self.assertEqual(len(pulse.sink_list()), 2)
    idx = pulse.module_load('module-null-sink')
    self.assertEqual(len(pulse.sink_list()), 3)
    pulse.module_unload(idx)
    self.assertEqual(len(pulse.sink_list()), 2)
    with self.assertRaises(pulsectl.PulseError):
      pulse.module_load('module-that-does-not-exist')
    self.assertEqual(len(pulse.sink_list()), 2)
mk-fg commented 2 years ago

But of course that's a fix for returning 4294967295 indexes without loading a module - why pulse does not load a module should be logged by default as something like this:

E: [pulseaudio] ltdl-bind-now.c: Failed to open module module-that-does-not-exist.so: module-that-does-not-exist.so: cannot open shared object file: No such file or directory
E: [pulseaudio] module.c: Failed to open module "module-that-does-not-exist".

In your case it's probably something different, like maybe you need to restart the daemon after package update (incompatible module versions), them being shipped in a separate package or not built/installed for some reason, or something else entirely.

jonathanjsimon commented 2 years ago

Yea, this is entirely on me. I had set disallow-module-loading in my systemd service file and forgotten about. Apologies.

mk-fg commented 2 years ago

No worries, it was still a perfectly valid bug that confused you due to lack of proper exception, not entirely your fault. Thanks for reporting!