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

method connect_to_cli use always using a unix socket #5

Closed xavierh closed 8 years ago

xavierh commented 8 years ago

Hi,

The method connect_to_cli of the class Pulse is using a Unix socket even when instantiating with an Ip address: with Pulse(server='192.168.0.2') as pulse: pulse.connect_to_cli() #Connect to the local pulse audio server

Took me a little while to understand why my code was not doing what I wanted.

On a side not, I am new to python and pulseaudio, I used connect_to_cli because I needed to change the proplist of a sink-input. How easy would it be to implement this ?

Regards, Xavier

mk-fg commented 8 years ago

Good point. connect_to_cli probably shouldn't be a method at all, but rather a standalone function, given that it has nothing to do with regular pulseaudio client. And an option to connect over tcp wouldn't hurt, I guess.

Should be easy to wrap proplist update funcs in the lib as well.

Will do both, maybe, but in the meantime, you can use python socket module to just connect to module-cli-tcp ip/port (nothing special there, don't need any signals as used in connect_to_cli), and send cli commands there (same as you'd do with unix socket), as I don't think pacmd supports anything but unix sockets either.

mk-fg commented 8 years ago

connect_to_cli probably shouldn't be a method at all, but rather a standalone function, given that it has nothing to do with regular pulseaudio client.

Now can be called only as pulsectl.connect_to_cli(...), not from Pulse instance, to avoid the confusion with regular (native-protocol) client.

And an option to connect over tcp wouldn't hurt, I guess.

"server" option in connect_to_cli can now be used to specify either absolute path to non-standard unix socket, module-cli-protocol-tcp host (with default port, names are fine too) or custom remote host/port passed as tuple.

E.g. pulsectl.connect_to_cli('192.168.0.2'), pulsectl.connect_to_cli('myhost.com') or pulsectl.connect_to_cli(('::1', 1234)).

Should be easy to wrap proplist update funcs in the lib as well.

Unfortunately, can't be done with regular native-protocol introspection api, it seems - couldn't find any proplist-update funcs implemented, that'd work with client context and "info" objects or their indices.

Can only be done through DBus API (such updates are implemented there) or cli, as far as I can tell.

mk-fg commented 8 years ago

Should be easy to wrap proplist update funcs in the lib as well.

Can only be done through DBus API (such updates are implemented there) or cli, as far as I can tell.

Maybe worth mentioning here that last time I checked, DBus API was deprecated and that module issued warning about stability issues, so you might be better off using cli (as you wanted), or seeing about fixing that (e.g. open issue, or maybe submit a patch) in pulseaudio introspection api, if this stuff is simply not implemented there, and cli is not good enough for your purposes.

Also, as you seem to be using module-cli over tcp - note that there's no authorization there, so leaving that port exposed on some public network is likely a bad idea.

mk-fg commented 8 years ago

I did ask whether the thing was possible to do through native api (in case I missed it) in #pulseaudio (on freenode irc), and apparently it's not implemented on purpose:

<MK_FG> I couldn't find a way to update proplists for sinks/streams/etc via
  libpulse and native-protocol client, am I correct that it's not possible
  outside of cli or dbus apis?
<tanuk> MK_FG: A client can only modify the proplist of itself (the client
  object) and the streams the client creates.
<tanuk> pacmd can indeed change anything. I don't think it should be able to
  do that, but oh well...

@xavierh I wonder, what did you want to update in the proplists? Not that I have anything against it, just can't seem to come up with any good reason, so quite curious.

xavierh commented 8 years ago

I use doing a multi stream multi room sound system at home. For that I need gmediarender, pulseaudio, openhab and python-pulse-control. I use openhab to choose which stream is playing where and python-pulse-control manage the different pulseaudio object needed for the streaming transmitter and receivers On the server per stream have: gmediarender -> Sink Stream 1 transmitter -> rtp stream (module-rtp-send)

On the Client per stream I HAD: rtp receiver (module-rtp-send) -> Sink Stream 1 Receiver -> loop back -> Real Sink output I simplified it buy doing (loosing the named sink at the same time): rtp receiver (module-rtp-recv) -> Real Sink output

My python code using your library is responsible:

The reason why I want to rename media.name using proplist is because openhab use the media.name to identify a sink-input. Unfortunately the name is not unique:

Sink Input #68
    Driver: module-rtp-recv.c
    Owner Module: 24
    Client: n/a
    Sink: 2
    Sample Specification: s16be 2ch 44100Hz
    Channel Map: front-left,front-right
    Format: pcm, format.sample_format = "\"s16be\""  format.rate = "44100"  format.channels = "2"
    Corked: no
    Mute: no
    Volume: 0: 100% 1: 100%
            0: 0.00 dB 1: 0.00 dB
            balance 0.00
    Buffer Latency: 0 usec
    Sink Latency: 234611 usec
    Resample method: speex-float-1
    Properties:
        media.role = "stream"
        media.name = "RTP Stream (PulseAudio RTP Stream on xavier-OptiPlex-390)"
        rtp.session = "PulseAudio RTP Stream on xavier-OptiPlex-390"
        rtp.origin = "xavier 3675682173 0 IN IP4 192.168.30.57"
        rtp.payload = "10"
        module-stream-restore.id = "sink-input-by-media-role:stream"

When I have more than one stream transmitted by same computer (using different sap address), I can not distinguish which one is which one and openhab think there is only one

To fix this issue I can:

mk-fg commented 8 years ago

Patch pulseaudio module-rtp-recv to have the sap address in the media name (I am not planning to recompile it for each different system but I might create an issue about it)

Note that media.name is derived in module-rtp-recv.c from sdp_info->session_name.

Which, in turn, set in module-rtp-send.c as:

n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn)));

So, if I'm understanding this correctly, and issue comes from more than one stream transmitted by same computer all having same "s=" in SDP packets, you can patch module-rtp-send.c (to include some stream-id alongside fqdn hostname) instead of recv, though not sure if it'd be easier - depends on whther you have multiple transmitters or just one, I guess.

Given that it might be rather trivial change, and seem really important for case with multiple rtp streams in the same network, might be worth adding some "session_name=" parameter to module-rtp-send.c and proposing the patch to pulseaudio upstream, sounds like a good thing to have some control over session name like that.

mk-fg commented 8 years ago

But on the other hand, if renaming works, can be good enough solution, doubt anyone would bother removing that ;) Thanks for the info!

xavierh commented 8 years ago

You are absolutely right. I am planning to use only one server with multiple stream. Which mean potentially patching only once. as session_name parameter seams to be the best solution as it is flexible and easy to implement. I will have a go at it later on. For the meantime, changing the media.name on the receiver does the trick and the media.name persist even when the sender unload/reload the module-rtp-send

Thank you for pointing me in the right direction