11 releases (7 breaking)
0.8.0 | Feb 6, 2024 |
---|---|
0.7.2 | Aug 24, 2023 |
0.6.0 | Feb 12, 2023 |
0.5.0 | Mar 22, 2022 |
0.1.0 | Aug 13, 2019 |
#28 in Multimedia
12,645 downloads per month
Used in 9 crates
(8 directly)
490KB
11K
SLoC
pipewire
PipeWire bindings for Rust.
These bindings are providing a safe API that can be used to interface with PipeWire.
Documentation
See the crate documentation.
lib.rs
:
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::{main_loop::MainLoop, context::Context};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mainloop = MainLoop::new(None)?;
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::{main_loop::MainLoop, context::Context};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mainloop = MainLoop::new(None)?;
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.
# // FIXME: Find a better method for this example we can actually call.
// 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::Rc
s 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:
use pipewire::main_loop::MainLoop;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mainloop = MainLoop::new(None)?;
let timer = mainloop.loop_().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.
Using similar methods, you can also react to IO or Signals, or call a callback whenever the loop is idle.
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.
Dependencies
~3–7MB
~128K SLoC