110 lines
2.6 KiB
Rust
110 lines
2.6 KiB
Rust
use pipewire::{context::Context, main_loop::MainLoop};
|
|
use std::{
|
|
net::{Ipv6Addr, SocketAddrV6},
|
|
sync::{Arc, RwLock},
|
|
};
|
|
use tokio::task::spawn_blocking;
|
|
use warp::{serve, Filter};
|
|
|
|
struct State_ {
|
|
device_list: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct State {
|
|
internal: Arc<RwLock<State_>>,
|
|
}
|
|
|
|
impl State {
|
|
fn new() -> State {
|
|
let internal = State_ {
|
|
device_list: vec![],
|
|
};
|
|
State {
|
|
internal: Arc::new(RwLock::new(internal)),
|
|
}
|
|
}
|
|
|
|
fn add_audio(&self, device: String) {
|
|
let mut st = self.internal.write().unwrap();
|
|
(*st).device_list.push(device);
|
|
}
|
|
|
|
fn audio_devices(&self) -> Vec<String> {
|
|
let st = self.internal.read().unwrap();
|
|
(*st).device_list.clone()
|
|
}
|
|
}
|
|
|
|
impl Default for State {
|
|
fn default() -> State {
|
|
State::new()
|
|
}
|
|
}
|
|
|
|
async fn server_main(state: State) {
|
|
let localhost: Ipv6Addr = "::1".parse().unwrap();
|
|
let server_addr = SocketAddrV6::new(localhost, 3001, 0, 0);
|
|
|
|
let root = warp::path!().map(|| "ok".to_string());
|
|
let list_output_devices = warp::path!("output_devices").map({
|
|
let state = state.clone();
|
|
move || {
|
|
let devices = state.audio_devices();
|
|
serde_json::to_string(&devices).unwrap()
|
|
}
|
|
});
|
|
|
|
let routes = root.or(list_output_devices);
|
|
|
|
serve(routes).run(server_addr).await;
|
|
}
|
|
|
|
fn handle_add_audio_device(state: State, props: &pipewire::spa::utils::dict::DictRef)
|
|
{
|
|
if props.get("media.class") == Some("Audio/Sink") {
|
|
if let Some(device_name) = props.get("node.description") {
|
|
state.add_audio(device_name.to_owned());
|
|
}
|
|
}
|
|
}
|
|
|
|
fn pipewire_loop(state: State) -> 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()?;
|
|
|
|
let _listener = registry
|
|
.add_listener_local()
|
|
.global({
|
|
let state = state.clone();
|
|
move |global_data| {
|
|
if let Some(props) = global_data.props {
|
|
handle_add_audio_device(state.clone(), props);
|
|
}
|
|
}
|
|
})
|
|
.register();
|
|
|
|
mainloop.run();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn pipewire_main(state: State) {
|
|
pipewire_loop(state).expect("pipewire should not error");
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let state = State::default();
|
|
|
|
spawn_blocking({
|
|
let state = state.clone();
|
|
move || pipewire_main(state)
|
|
});
|
|
|
|
server_main(state.clone()).await;
|
|
}
|