-
Notifications
You must be signed in to change notification settings - Fork 365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PipeWire host #651
Draft
m00nwtchr
wants to merge
9
commits into
RustAudio:master
Choose a base branch
from
m00nwtchr:pipewire-host
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
PipeWire host #651
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
9b67d2e
platform: Conditional impl_platform_host based on feature flags
ishitatsuyuki c8c3281
Merge pull request #653 from ishitatsuyuki/feature-platform-impl
ishitatsuyuki 8f0ca29
Pipewire host & device
m00nwtchr b5263ee
stream code base copied from jack host, nonfunctional
m00nwtchr 15d7987
some fixes, node creation
m00nwtchr 8f0bba1
cleanup, keep references to node/setting proxies and listeners, remov…
m00nwtchr 05dd143
make stream.rs compile without jack feature
m00nwtchr 61aa078
keep references to node listeners
m00nwtchr 854a644
Merge branch 'master' into pipewire-host
m00nwtchr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
extern crate pipewire; | ||
|
||
use self::pipewire::{ | ||
metadata::{Metadata, MetadataListener}, | ||
node::{Node, NodeListener}, | ||
prelude::*, | ||
proxy::Listener, | ||
registry::{GlobalObject, Registry}, | ||
spa::{Direction, ForeignDict}, | ||
types::ObjectType, | ||
Core, MainLoop, | ||
}; | ||
|
||
use std::{ | ||
borrow::BorrowMut, | ||
cell::{Cell, RefCell}, | ||
collections::HashMap, | ||
rc::Rc, | ||
sync::mpsc, | ||
thread, | ||
time::Duration, | ||
}; | ||
|
||
use super::device::DeviceType; | ||
|
||
#[derive(Debug)] | ||
enum Message { | ||
Terminate, | ||
GetSettings, | ||
CreateDeviceNode { | ||
name: String, | ||
device_type: DeviceType, | ||
autoconnect: bool, | ||
}, | ||
} | ||
|
||
enum MessageRepl { | ||
Settings(Settings), | ||
NodeInfo(NodeInfo), | ||
} | ||
|
||
pub struct NodeInfo { | ||
pub name: String, | ||
} | ||
|
||
pub struct PWClient { | ||
pw_sender: pipewire::channel::Sender<Message>, | ||
main_receiver: mpsc::Receiver<MessageRepl>, | ||
} | ||
|
||
impl PWClient { | ||
pub fn new() -> Self { | ||
let (main_sender, main_receiver) = mpsc::channel(); | ||
let (pw_sender, pw_receiver) = pipewire::channel::channel(); | ||
|
||
let _pw_thread = thread::spawn(move || pw_thread(main_sender, pw_receiver)); | ||
|
||
Self { | ||
pw_sender, | ||
main_receiver, | ||
} | ||
} | ||
|
||
pub fn get_settings(&self) -> Result<Settings, String> { | ||
match self.pw_sender.send(Message::GetSettings) { | ||
Ok(_) => match self.main_receiver.recv() { | ||
Ok(MessageRepl::Settings(settings)) => Ok(settings), | ||
Err(err) => Err(format!("{:?}", err)), | ||
_ => Err(format!("")), | ||
}, | ||
Err(err) => Err(format!("{:?}", err)), | ||
} | ||
} | ||
|
||
pub fn create_device_node( | ||
&self, | ||
name: String, | ||
device_type: DeviceType, | ||
connect_ports_automatically: bool, | ||
) -> Result<NodeInfo, String> { | ||
match self.pw_sender.send(Message::CreateDeviceNode { | ||
name, | ||
device_type, | ||
autoconnect: connect_ports_automatically, | ||
}) { | ||
Ok(_) => match self.main_receiver.recv() { | ||
Ok(MessageRepl::NodeInfo(info)) => Ok(info), | ||
Err(err) => Err(format!("{:?}", err)), | ||
_ => Err(format!("")), | ||
}, | ||
Err(err) => Err(format!("{:?}", err)), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
struct State { | ||
settings: Settings, | ||
nodes: Vec<ProxyItem>, | ||
} | ||
|
||
#[derive(Default, Clone, Debug)] | ||
pub struct Settings { | ||
pub sample_rate: u32, | ||
pub min_buffer_size: u32, | ||
pub max_buffer_size: u32, | ||
pub default_buffer_size: u32, | ||
} | ||
|
||
enum ProxyItem { | ||
Metadata { | ||
_proxy: Metadata, | ||
_listener: MetadataListener, | ||
}, | ||
Node { | ||
_proxy: Node, | ||
_listener: NodeListener, | ||
}, | ||
} | ||
|
||
fn pw_thread( | ||
main_sender: mpsc::Sender<MessageRepl>, | ||
pw_receiver: pipewire::channel::Receiver<Message>, | ||
) { | ||
pipewire::init(); | ||
// let state = Rc::new(State::default()); | ||
let state = Rc::new(RefCell::new(State::default())); | ||
let proxies = Rc::new(RefCell::new(HashMap::new())); | ||
|
||
let mainloop = pipewire::MainLoop::new().expect("Failed to create PipeWire Mainloop"); | ||
|
||
let context = pipewire::Context::new(&mainloop).expect("Failed to create PipeWire Context"); | ||
let core = Rc::new( | ||
context | ||
.connect(None) | ||
.expect("Failed to connect to PipeWire"), | ||
); | ||
let registry = Rc::new(core.get_registry().expect("Failed to get Registry")); | ||
|
||
let _receiver = pw_receiver.attach(&mainloop, { | ||
let mainloop = mainloop.clone(); | ||
let state = state.clone(); | ||
let main_sender = main_sender.clone(); | ||
let core = core.clone(); | ||
let proxies = proxies.clone(); | ||
|
||
move |msg| match msg { | ||
Message::Terminate => mainloop.quit(), | ||
Message::GetSettings => { | ||
let settings = state.borrow().settings.clone(); | ||
main_sender.send(MessageRepl::Settings(settings)); | ||
} | ||
Message::CreateDeviceNode { | ||
name, | ||
device_type, | ||
autoconnect, | ||
} => { | ||
let node: Node = core | ||
.create_object( | ||
"adapter", //node_factory.get().expect("No node factory found"), | ||
&pipewire::properties! { | ||
*pipewire::keys::NODE_NAME => name.clone(), | ||
*pipewire::keys::FACTORY_NAME => "support.null-audio-sink", | ||
*pipewire::keys::MEDIA_TYPE => "Audio", | ||
*pipewire::keys::MEDIA_CATEGORY => match device_type { | ||
DeviceType::InputDevice => "Capture", | ||
DeviceType::OutputDevice => "Playback" | ||
}, | ||
*pipewire::keys::NODE_AUTOCONNECT => match autoconnect { | ||
false => "false", | ||
true => "true", | ||
}, | ||
// Don't remove the object on the remote when we destroy our proxy. | ||
// *pipewire::keys::OBJECT_LINGER => "1" | ||
}, | ||
) | ||
.expect("Failed to create object"); | ||
|
||
let _listener = node | ||
.add_listener_local() | ||
.info(|info| { | ||
// println!("{:?}", info); | ||
}) | ||
.param(|a, b, c, d| { | ||
println!("{}, {}, {}, {}", a, b, c, d); | ||
}) | ||
.register(); | ||
|
||
println!("{:?}", node); | ||
|
||
state.as_ref().borrow_mut().nodes.push(ProxyItem::Node { | ||
_proxy: node, | ||
_listener, | ||
}); | ||
|
||
main_sender.send(MessageRepl::NodeInfo(NodeInfo { name })); | ||
} | ||
} | ||
}); | ||
|
||
let _reg_listener = registry | ||
.add_listener_local() | ||
.global({ | ||
let state = state.clone(); | ||
let registry = registry.clone(); | ||
let proxies = proxies.clone(); | ||
|
||
move |global| match global.type_ { | ||
ObjectType::Metadata => handle_metadata(global, &state, ®istry, &proxies), | ||
_ => {} | ||
} | ||
}) | ||
.register(); | ||
|
||
// let timer = mainloop.add_timer({ | ||
// move |_| { | ||
// } | ||
// }); | ||
|
||
// timer | ||
// .update_timer( | ||
// Some(Duration::from_millis(500)), | ||
// Some(Duration::from_secs(1)), | ||
// ) | ||
// .into_result() | ||
// .expect("FU"); | ||
|
||
mainloop.run(); | ||
} | ||
|
||
fn handle_metadata( | ||
metadata: &GlobalObject<ForeignDict>, | ||
state: &Rc<RefCell<State>>, | ||
registry: &Rc<Registry>, | ||
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>, | ||
) { | ||
let props = metadata | ||
.props | ||
.as_ref() | ||
.expect("Metadata object is missing properties"); | ||
|
||
match props.get("metadata.name") { | ||
Some("settings") => { | ||
let settings: Metadata = registry.bind(metadata).expect("Metadata"); | ||
|
||
let _listener = settings | ||
.add_listener_local() | ||
.property({ | ||
let state = state.clone(); | ||
move |_, key, _, value| { | ||
let mut state = state.as_ref().borrow_mut(); | ||
if let Some(value) = value { | ||
if let Ok(value) = value.parse::<u32>() { | ||
match key { | ||
Some("clock.rate") => state.settings.sample_rate = value, | ||
Some("clock.quantum") => { | ||
state.settings.default_buffer_size = value | ||
} | ||
Some("clock.min-quantum") => { | ||
state.settings.min_buffer_size = value | ||
} | ||
Some("clock.max-quantum") => { | ||
state.settings.max_buffer_size = value | ||
} | ||
_ => {} | ||
}; | ||
} | ||
} | ||
0 | ||
} | ||
}) | ||
.register(); | ||
|
||
proxies.as_ref().borrow_mut().insert( | ||
metadata.id, | ||
ProxyItem::Metadata { | ||
_proxy: settings, | ||
_listener, | ||
}, | ||
); | ||
} | ||
_ => {} | ||
}; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably doesn't matter since a git dependency can't be included in a published crate anyway, but you should suffix it the URL with
.git
so GitLab doesn't complain about that.