Skip to content

Conversation

ggiraudon
Copy link

For your consideration : My implementation of a native pipewire audio-backend.

@Copilot Copilot AI review requested due to automatic review settings October 4, 2025 20:18
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds a PipeWire audio backend to provide native support for the modern PipeWire audio server on Linux systems, offering low-latency audio playback as an alternative to existing backends.

  • Implements a complete PipeWire audio sink with proper stream management and error handling
  • Adds feature flags and dependency configuration for the new backend
  • Integrates the backend into the existing audio backend selection system

Reviewed Changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.

File Description
playback/src/audio_backend/pipewire.rs Complete PipeWire sink implementation with audio format conversion and stream handling
playback/src/audio_backend/mod.rs Registers the PipeWire backend in the available backends list
playback/Cargo.toml Adds pipewire and libspa dependencies for the playback crate
Cargo.toml Defines the pipewire-backend feature and workspace-level dependencies

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 165 to 167
for &byte in data {
sender.send(byte).map_err(|_| PipeWireError::NotConnected)?;
}
Copy link
Preview

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sending audio data byte-by-byte through a channel is highly inefficient and will likely cause audio dropouts. Consider sending data in chunks or using a different approach like a ring buffer for better performance.

Copilot uses AI. Check for mistakes.

F32 => spa::param::audio::AudioFormat::F32LE,
S32 => spa::param::audio::AudioFormat::S32LE,
S24 => spa::param::audio::AudioFormat::S24LE,
S24_3 => spa::param::audio::AudioFormat::S24_32LE,
Copy link
Preview

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The audio format mapping for S24_3 appears incorrect. S24_3 typically represents 24-bit samples packed in 3 bytes, but S24_32LE represents 24-bit samples in 32-bit containers. This should likely be S24_3LE if available.

Suggested change
S24_3 => spa::param::audio::AudioFormat::S24_32LE,
S24_3 => spa::param::audio::AudioFormat::S24_3LE,

Copilot uses AI. Check for mistakes.

Comment on lines 247 to 255
for i in 0..total_bytes {
match receiver.try_recv() {
Ok(byte) => {
slice[i] = byte;
bytes_read += 1;
}
Err(_) => break,
}
}
Copy link
Preview

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading audio data byte-by-byte in the audio callback will cause significant performance issues and audio dropouts. The audio callback should be lock-free and operate on larger chunks of data.

Copilot uses AI. Check for mistakes.

self.initialized = true;

// Give the thread a moment to initialize
thread::sleep(std::time::Duration::from_millis(100));
Copy link
Preview

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a fixed sleep duration for initialization is unreliable. Consider using proper synchronization mechanisms like condition variables or status callbacks to ensure the PipeWire stream is actually ready before returning.

Copilot uses AI. Check for mistakes.

// Wait for the thread to finish with a timeout
if let Some(handle) = self._main_loop_handle.take() {
// Give it a moment to exit gracefully
thread::sleep(std::time::Duration::from_millis(100));
Copy link
Preview

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a fixed sleep duration before joining the thread is unreliable. The thread should signal when it's ready to exit, or use a timeout with the join operation.

Copilot uses AI. Check for mistakes.

Copy link
Member

@roderickvd roderickvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we will consider 😆 very nice addition. It needs work; also take a look at some of the Copilot suggestions which seem to make sense to me.

Don't forget to add a changelog entry too.

Cargo.toml Outdated
] }
url = "2.2"

# Dependencies for pipewire
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should go to playback/Cargo.toml.

@ggiraudon
Copy link
Author

Hi Roderick,
I might need your assistance on some of it as I'm not an rust expert.
I tried implementing various buffering methods but always end up with choppy/unreliable playback when i do so and I'm not sure what I'm doing wrong.
On paper things should be more efficient when passing stuff in chunks instead of spoon-feeding the channels byte by byte but in practice I can't seem to get it to work.
I've created a branch called pipewire-buffer-test in my fork that is my attempt at such improvement but the playback performance is horrible (choppy at random) even when compiled in release mode.
The current implementation in the dev branch seems to be the one behaving the best so far but I know its not optimal.

@roderickvd
Copy link
Member

First a bit of a low-effort but hopefully high-impact reply: does looking at RustAudio/cpal#938 help? That implementation is not perfect, but the use of pipewire::buffer::Buffer seems interesting.

In your fork I see that you're pushing a Vec<u8> through a sync channel. I suspect that will have poor performance too. You may want to look like at using a ring buffer (ringbuf crate or rtrb if you don't need to work across threads).

Hope this helps.

@ggiraudon ggiraudon requested a review from roderickvd October 7, 2025 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants