Conversation
19070c2 to
1eca9ce
Compare
9e2ef9f to
fd59a29
Compare
|
Seems pipewire need edition 2024.. |
|
@roderickvd can you help review this pr? Thanks, and when can this crate be upgraded to edition 2024? I would also want to help |
Definitely will help you review it. Need a bit more time.
Actually cpal itself doesn't need to be upgraded to Rust 2024, it just needs a MSRV of Rust of 1.85 or higher to support dependencies that are Rust 2024 already. When we can I'd like to stick cpal to Rust 2021 so we keep our MSRV down. |
|
Super cool man! Happy to see cpal having better linux support, opening the possibility of loopback recording on Linux! |
07bc999 to
6cea2ad
Compare
|
ok, only one ci that I cannot fix |
a81e104 to
5a3817c
Compare
|
ok cross-rs based on ubuntu20.04, so |
721beb0 to
424d78f
Compare
|
How does this differ from #692? |
it is meaningful, but not used in this repo
no use to keep an unnecessary function
PipeWire does support U64 and I64, but libspa doesn't yet. so I add a todo and a note
make the flag pipewire, beep and pulseaudio enabled the same time
and cross --all-features also with pulseaudio
it should not be here
and add some documents
we do not need object_id and node_id in this place and no need for functions channels, node_name and direction
092ec59 to
0ba60e9
Compare
add documents add CHANGELOG
roderickvd
left a comment
There was a problem hiding this comment.
Really almost there 😆
Thanks for sticking through all this!
| let current_channels = user_data.format.channels(); | ||
| let current_rate = user_data.format.rate(); | ||
| if current_channels != channels || rate != current_rate { | ||
| (user_data.error_callback)(StreamError::BackendSpecific { |
There was a problem hiding this comment.
We may want to call stream.disconnect() or at least stream.set_active(false) on such an event. Because in the callback the n_channels is queried, and if we leave that running, then the data slice will be different from what the user expects.
There was a problem hiding this comment.
ok, I set inactive when the values do not match.
src/host/pipewire/mod.rs
Outdated
| mod stream; | ||
| mod utils; | ||
|
|
||
| // just init the pipewire the check if it is available |
There was a problem hiding this comment.
Comment can be removed; function is self-explanatory.
src/host/pipewire/device.rs
Outdated
| channels: ChannelCount, | ||
| rate: SampleRate, | ||
| allow_rates: Vec<SampleRate>, | ||
| quantum: u32, |
There was a problem hiding this comment.
This should be of type FrameCount too.
src/host/pipewire/device.rs
Outdated
| &self.node_name | ||
| } | ||
|
|
||
| pub fn quantum(&self) -> FrameCount { |
There was a problem hiding this comment.
I still think we should remove rate() and allow_rates() because they can be gotten from supported_*_configs already. quantum() is OK for now but I want to replace with the buffer size querying PR soon after.
| // | ||
| // This ensures positive values that are compatible with our `StreamInstant` representation. | ||
| #[inline] | ||
| fn stream_timestamp_fallback( |
There was a problem hiding this comment.
It's called "fallback" but do we ever use true PipeWire timestamps elsewhere with https://pipewire.pages.freedesktop.org/pipewire-rs/pipewire_sys/fn.pw_stream_get_time_n.html? We should: it's RT-safe and more accurate.
LLM-assisted sketch:
/// Hardware timestamp from a PipeWire graph cycle.
struct PwTime {
/// CLOCK_MONOTONIC nanoseconds, stamped at the start of the graph cycle.
now_ns: i64,
/// Pipeline delay converted to nanoseconds.
/// For output: how far ahead of the driver our next sample will be played.
/// For input: how long ago the data in the buffer was captured.
delay_ns: i64,
}
/// Returns a hardware timestamp for the current graph cycle, or `None` if
/// the driver has not started yet or the rate is unavailable.
fn pw_stream_time(stream: &pw::stream::StreamRc) -> Option<(StreamInstant, i64)> {
let mut t: pw_sys::pw_time = unsafe { mem::zeroed() };
let rc = unsafe {
pw_sys::pw_stream_get_time_n(
stream.as_raw_ptr(),
&mut t,
mem::size_of::<pw_sys::pw_time>(),
)
};
if rc != 0 || t.now == 0 || t.rate.denom == 0 {
return None;
}
debug_assert_eq!(t.rate.num, 1, "unexpected pw_time rate.num");
let delay_ns = t.delay * 1_000_000_000i64 / t.rate.denom as i64;
let callback = crate::StreamInstant::from_nanos(t.now);
Some((callback, delay_ns))
}This may require features = ["v0_3_50"].
Then the StreamInstant docs should be updated to include PipeWire support.
|
One more thing I just thought of: before passing the data buffer to the output callback, that buffer needs to be zeroed. More precisely: it needs to be set to |
In case that something happens to pipewire, we cannot use expect
for example let Some(samples) = buf_data.data() else {
return;
};
let len = samples.len();
let silence_template = vec![0_u8; len];
samples.copy_from_slice(&silence_template);like this way? I do not know how to get the length of sample before the stream start.. |
No that would allocate instead try calling https://doc.rust-lang.org/stable/std/primitive.slice.html#method.fill on the slice with |
thank you very much, done |
Add support to pipewire
You can test it with pipewire feature open
Pipewire support use config to define rates. So the default config of cpal with pipewire can be changed through config like following.
Still problems left:*
once we do 'pipewire::init', we can only dequeue it after the whole thread. even put the function to another thread, the function still works.. But seems we can run init many times..(Ok, seems it is not a problem, because in when we call init, the init action will only be called once. I think it will be ok)*
The crates by pipewire need edition 2024. I think that should be another pr.