Selkies
Developer Reference

audio_control

PulseAudio/PipeWire control plane shared by the WebSocket and WebRTC transports.

Both transports need the same few one-shot control operations around the pcmflux data planes: make sure the null sink whose monitor the server captures exists, resolve the capture source, bring up the SelkiesVirtualMic (the input null sink plus module-virtual-source) for microphone forwarding, set the system defaults, and move a strayed pcmflux source-output back onto its source. AudioControl does all of it in-process over pulsectl_asyncio; the pactl subprocess backend is engaged only when the bindings are missing or the server connection cannot be made, and says so in one log line.

Safety contract with the ctypes bindings: libpulse keeps raw function pointers to per-operation callback trampolines that live exactly as long as the awaiting coroutine frame, so cancelling a pending operation (a timeout, a peer teardown) lets the server's late reply jump into freed memory. Every operation therefore runs in a task of its own that is never cancelled: a timeout abandons the connection instead (libpulse drops the pending operations' callbacks on disconnect) and the task is left to finish by itself, and every client is closed through aclose rather than garbage-collected with its state callback still registered. A client binds to the event loop it first runs on and refuses use from any other loop or thread.

attributePULSE_AVAILABLE
= True
attributelogger
= logging.getLogger('audio_control')
attributeT
= TypeVar('T')
attributeDEFAULT_CAPTURE_SINK
= 'output'
attributeVIRTUAL_MIC_SOURCE
= 'SelkiesVirtualMic'
attributeVIRTUAL_MIC_SINK
= 'input'
attributeVIRTUAL_MIC_MASTER
= f'{VIRTUAL_MIC_SINK}.monitor'
attributePIPEWIRE_NULL_MONITOR
= 'auto_null.monitor'
attributeVIRTUAL_MIC_SOURCE_NAMESTuple[str, ...]
= (VIRTUAL_MIC_SOURCE, f'output.{VIRTUAL_MIC_SOURCE}')
attributePCMFLUX_APP_NAME
= 'pcmflux'

On this page

No Headings
Edit on GitHub