Crate pipewire[][src]

Expand description

Rust bindings for pipewire

pipewire is a crate offering a rustic bindings for libpipewire, the library for interacting with the pipewire server.

Programs that interact with pipewire usually react to events from the server by registering callbacks and invoke methods on objects on the server by calling methods on local proxy objects.

Getting started

Most programs that interact with pipewire will need the same few basic objects:

  • A MainLoop that drives the program, reacting to any incoming events and dispatching method calls. Most of a time, the program/thread will sit idle in this loop, waiting on events to occur.
  • A Context that keeps track of any pipewire resources.
  • A Core that is a proxy for the remote pipewire instance, used to send messages to and receive events from the remote server.
  • Optionally, a Registry that can be used to manage and track available objects on the server.

This is how they can be created:

use pipewire::{MainLoop, Context};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mainloop = MainLoop::new()?;
    let context = Context::new(&mainloop)?;
    let core = context.connect(None)?;
    let registry = core.get_registry()?;

    Ok(())
}

Now you can start hooking up different kinds of callbacks to the objects to react to events, and call methods on objects to change the state of the remote.

use pipewire::{MainLoop, Context};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mainloop = MainLoop::new()?;
    let context = Context::new(&mainloop)?;
    let core = context.connect(None)?;
    let registry = core.get_registry()?;

    // Register a callback to the `global` event on the registry, which notifies of any new global objects
    // appearing on the remote.
    // The callback will only get called as long as we keep the returned listener alive.
    let _listener = registry
        .add_listener_local()
        .global(|global| println!("New global: {:?}", global))
        .register();

    // Calling the `destroy_global` method on the registry will destroy the object with the specified id on the remote.
    // We don't have a specific object to destroy now, so this is commented out.
    // registry.destroy_global(313).into_result()?;

    mainloop.run();

    Ok(())
}

Note that registering any callback requires the closure to have the 'static lifetime, so if you need to capture any variables, use move || closures, and use std::rc::Rcs to access shared variables and some std::cell variant if you need to mutate them.

Also note that we called mainloop.run() at the end. This will enter the loop, and won’t return until we call mainloop.quit() from some event. If we didn’t run the loop, events and method invocations would not be processed, so the program would terminate without doing much.

The main loop

Sometimes, other stuff needs to be done even though we are waiting inside the main loop.
This can be done by adding sources to the loop.

For example, we can call a function on an interval:

// We also need to include the `Loop` trait for this.
use pipewire::{MainLoop, Loop};
use std::time::Duration;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mainloop = MainLoop::new()?;

    let timer = mainloop.add_timer(|_| println!("Hello"));
    // Call the first time in half a second, and then in a one second interval.
    timer.update_timer(Some(Duration::from_millis(500)), Some(Duration::from_secs(1))).into_result()?;

    mainloop.run();

    Ok(())
}

This program will print out “Hello” every second forever.

You can also react to IO or Signals using similar methods on the Loop trait.

Multithreading

The pipewire library is not really thread-safe, so pipewire objects do not implement Send or Sync.

However, you can spawn a MainLoop in another thread and do bidirectional communication using two channels.

To send messages to the main thread, we can easily use a std::sync::mpsc. Because we are stuck in the main loop in the pipewire thread and can’t just block on receiving a message, we use a pipewire::channel instead.

See the pipewire::channel module for details.

Re-exports

pub use spa;
pub use pw_sys as sys;

Modules

buffer
channel

This module provides a channel for communicating with a thread running a pipewire loop. The channel is split into two types, Sender and Receiver.

constants

Pipewire constants.

keys

A collection of keys that are used to add extra information on objects.

link
node
port
prelude
proxy
registry
stream

Pipewire Stream

types

Macros

properties

A macro for creating a new Properties struct with predefined key-value pairs.

Structs

ChangeMask
Context
Core
CoreInner
EventSource

A source that can be used to signal to a loop that an event has occurred.

Info
IoSource
Listener
ListenerLocalBuilder
MainLoop
MainLoopInner
Properties

A collection of key/value pairs.

PropertiesRef
SignalSource
TimerSource

A source that can be used to have a callback called on a timer.

WeakMainLoop

Enums

Error

Constants

PW_ID_CORE

Traits

IsASource
Loop

A trait for common functionality of the different pipewire loop kinds, most notably MainLoop.

Functions

deinit

Deinitialize PipeWire

init

Initialize PipeWire