pipewire/
lib.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4//! # Rust bindings for pipewire
5//! `pipewire` is a crate offering Rust bindings to `libpipewire`, the library for interacting
6//! with the pipewire server.
7//!
8//! Programs that interact with pipewire usually react to events from the server by registering callbacks
9//! and invoke methods on objects on the server by calling methods on local proxy objects.
10//!
11//! ## Getting started
12//! Most programs that interact with pipewire will need the same few basic objects:
13//! - A [`MainLoop`](`main_loop::MainLoop`) that drives the program, reacting to any incoming events and dispatching method calls.
14//!   Most of a time, the program/thread will sit idle in this loop, waiting on events to occur.
15//! - A [`Context`](`context::Context`) that keeps track of any pipewire resources.
16//! - A [`Core`](`core::Core`) that is a proxy for the remote pipewire instance, used to send messages to and receive events from the
17//!   remote server.
18//! - Optionally, a [`Registry`](`registry::Registry`) that can be used to manage and track available objects on the server.
19//!
20//! This is how they can be created:
21//! ```no_run
22//! use pipewire::{main_loop::MainLoopBox, context::ContextBox};
23//!
24//! fn main() -> Result<(), Box<dyn std::error::Error>> {
25//!     let mainloop = MainLoopBox::new(None)?;
26//!     let context = ContextBox::new(&mainloop.loop_(), None)?;
27//!     let core = context.connect(None)?;
28//!     let registry = core.get_registry()?;
29//!
30//!     Ok(())
31//! }
32//! ```
33//!
34//! ## Smart pointers to PipeWire objects
35//! The example above uses [`std::boxed::Box`]-like smart pointers to create the needed objects.
36//! Those boxes use lifetimes to ensure the objects dependencies (e.g. `Core` depends on `MainLoop`)
37//! outlive the object itself.
38//! If more flexibility is needed, [`std::rc::Rc`]-like reference-counting smart pointers also exist.
39//! Those will automatically keep the objects dependencies alive until the object is destroyed.
40//!
41//! Both of these kinds of types will automatically dereference to non-owning references for shared
42//! functionality, e.g. [`&MainLoop`](`main_loop::MainLoop`) or [`&Core`](`core::Core`).
43//!
44//! The same example as above, but using `Rc` types:
45//! ```no_run
46//! use pipewire::{main_loop::MainLoopRc, context::ContextRc};
47//!
48//! fn main() -> Result<(), Box<dyn std::error::Error>> {
49//!     let mainloop = MainLoopRc::new(None)?;
50//!     let context = ContextRc::new(&mainloop, None)?;
51//!     let core = context.connect_rc(None)?;
52//!     let registry = core.get_registry_rc()?;
53//!
54//!     Ok(())
55//! }
56//! ```
57//! ## Listening for events
58//! Once the needed objects are created, you can start hooking up different kinds of callbacks to
59//! them to react to events, and call methods to change the state of the remote.
60//! ```no_run
61//! use pipewire::{main_loop::MainLoopBox, context::ContextBox};
62//!
63//! fn main() -> Result<(), Box<dyn std::error::Error>> {
64//!     let mainloop = MainLoopBox::new(None)?;
65//!     let context = ContextBox::new(&mainloop.loop_(), None)?;
66//!     let core = context.connect(None)?;
67//!     let registry = core.get_registry()?;
68//!
69//!     // Register a callback to the `global` event on the registry, which notifies of any new global objects
70//!     // appearing on the remote.
71//!     // The callback will only get called as long as we keep the returned listener alive.
72//!     let _listener = registry
73//!         .add_listener_local()
74//!         .global(|global| println!("New global: {:?}", global))
75//!         .register();
76//!
77//!     // Calling the `destroy_global` method on the registry will destroy the object with the specified id on the remote.
78//!     // We don't have a specific object to destroy now, so this is commented out.
79//!     # // FIXME: Find a better method for this example we can actually call.
80//!     // registry.destroy_global(313).into_result()?;
81//!
82//!     mainloop.run();
83//!
84//!     Ok(())
85//! }
86//! ```
87//! Note that registering any callback requires the closure to have the `'static` lifetime, so if you need to capture
88//! any variables, use `move ||` closures, and use [`std::rc::Rc`]s to access shared variables
89//! and some [`std::cell`] variant if you need to mutate them.
90//!
91//! Also note that we called `mainloop.run()` at the end.
92//! This will enter the loop, and won't return until we call `mainloop.quit()` from some event.
93//! If we didn't run the loop, events and method invocations would not be processed, so the program would terminate
94//! without doing much.
95//!
96//! ## The main loop
97//! Sometimes, other stuff needs to be done even though we are waiting inside the main loop. \
98//! This can be done by adding sources to the loop.
99//!
100//! For example, we can call a function on an interval:
101//!
102//! ```no_run
103//! use pipewire::main_loop::MainLoopBox;
104//! use std::time::Duration;
105//!
106//! fn main() -> Result<(), Box<dyn std::error::Error>> {
107//!     let mainloop = MainLoopBox::new(None)?;
108//!
109//!     let timer = mainloop.loop_().add_timer(|_| println!("Hello"));
110//!     // Call the first time in half a second, and then in a one second interval.
111//!     timer.update_timer(Some(Duration::from_millis(500)), Some(Duration::from_secs(1))).into_result()?;
112//!
113//!     mainloop.run();
114//!
115//!     Ok(())
116//! }
117//! ```
118//! This program will print out "Hello" every second forever.
119//!
120//! Using similar methods, you can also react to IO or Signals, or call a callback whenever the loop is idle.
121//!
122//! ## Multithreading
123//! The pipewire library is not really thread-safe, so pipewire objects do not implement [`Send`](`std::marker::Send`)
124//! or [`Sync`](`std::marker::Sync`).
125//!
126//! However, you can spawn a [`MainLoop`](`main_loop::MainLoop`) in another thread and do bidirectional communication using two channels.
127//!
128//! To send messages to the main thread, we can easily use a [`std::sync::mpsc`].
129//! Because we are stuck in the main loop in the pipewire thread and can't just block on receiving a message,
130//! we use a [`pipewire::channel`](`crate::channel`) instead.
131//!
132//! See the [`pipewire::channel`](`crate::channel`) module for details.
133//!
134//! # Useful links
135//! For info on more general concepts about PipeWire as well as the C library `libpipewire`, see [PipeWire's
136//! documentation](https://docs.pipewire.org/). Some notable pages are:
137//!  - [The PipeWire overview](https://docs.pipewire.org/page_overview.html)
138//!  - [A short overview of PipeWire's design](https://docs.pipewire.org/page_design.html)
139//!  - [A design reference on the various objects that exist in PipeWire](https://docs.pipewire.org/page_objects_design.html)
140//!  - [The libpipewire overview](https://docs.pipewire.org/page_library.html)
141
142pub mod buffer;
143pub mod channel;
144pub mod client;
145pub mod constants;
146pub mod context;
147pub mod core;
148pub mod device;
149pub mod factory;
150pub mod keys;
151pub mod link;
152pub mod loop_;
153pub mod main_loop;
154pub mod metadata;
155pub mod module;
156pub mod node;
157pub mod permissions;
158pub mod port;
159pub mod properties;
160pub mod proxy;
161pub mod registry;
162pub mod stream;
163pub mod thread_loop;
164pub mod types;
165
166mod error;
167pub use error::*;
168
169mod utils;
170
171pub use pw_sys as sys;
172pub use spa;
173
174use std::ptr;
175
176/// Initialize PipeWire
177///
178/// Initialize the PipeWire system and set up debugging
179/// through the environment variable `PIPEWIRE_DEBUG`.
180pub fn init() {
181    use std::sync::OnceLock;
182    static INITIALIZED: OnceLock<()> = OnceLock::new();
183    INITIALIZED.get_or_init(|| unsafe { pw_sys::pw_init(ptr::null_mut(), ptr::null_mut()) });
184}
185
186/// Deinitialize PipeWire
187///
188/// # Safety
189/// This must only be called once during the lifetime of the process, once no PipeWire threads
190/// are running anymore and all PipeWire resources are released.
191pub unsafe fn deinit() {
192    pw_sys::pw_deinit()
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_init() {
201        init();
202        unsafe {
203            deinit();
204        }
205    }
206}