Skip to content

Conversation

DemiMarie
Copy link
Contributor

@DemiMarie DemiMarie commented Apr 29, 2022

This now actually works.

Fixes QubesOS/qubes-issues#6358

Edit: This builds ordinary Fedora/Debian/Arch packages. To use them, install them in your VM of choice, then enable PipeWire with qvm-service somevm pipewire on.

Edit 2: I have fixed all the bugs that I am aware of and able to find. There is still one remaining bug: if I am recording an audio stream, and start playback while the recording is in progress, audio playback may stop working. However, I believe this is not an agent bug, but rather a bug in pacat-simple-vchan, as I am also able to get pacat-simple-vchan to behave strangely in other ways (such as not responding to control requests while recording silence). Now fixed I think.

Edit 3: All remaining bugs appear to be fixed, at least if pacat-simple-vchan has QubesOS/qubes-gui-daemon#109.

Edit 4: Remaining to-do items (should not block merge):

  • Allow some properties (such as buffer sizes) properties to be configured via QubesDB as well as the configuration file.
  • Watch /qubes-audio-domain-xid for changes. If it does change, disconnect from the old audio VM and connect to the new one.
  • Provide an interface that an audio VM can use to differentiate between a microphone being muted and disconnected. Useful for workloads that care about this distinction, if any.
  • Allow the node name to be configured via the configuration file.
  • Integration with qubes-video-companion for video support.
  • Implement buffer negotiation as suggested in the header file.

@marmarek
Copy link
Member

marmarek commented May 4, 2022

PipelineRefresh

@DemiMarie
Copy link
Contributor Author

PipelineRetry

@DemiMarie DemiMarie marked this pull request as ready for review July 1, 2022 06:09
@DemiMarie
Copy link
Contributor Author

PipelineRefresh

@DemiMarie
Copy link
Contributor Author

PipelineRetry

@DemiMarie
Copy link
Contributor Author

At this point, the only code that I know to be missing is the Debian and Arch maintainer scripts to manage PipeWire-related systemd units.

break;
case SPA_PARAM_Props:;
_Static_assert(SPA_PARAM_Props == 2, "wrong SPA_PARAM_Props");
/* TODO: reconfigure the stream according to the new properties */
Copy link
Member

Choose a reason for hiding this comment

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

Is there anything that is actually worth handling (and is possible to do)? If so, maybe log a message for now? Otherwise, drop the comment or at least its "TODO" part.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I honestly do not know, hence the TODO.

Copy link
Contributor Author

@DemiMarie DemiMarie left a comment

Choose a reason for hiding this comment

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

Comments (hopefully) addressed. I will not be able to re-test until tomorrow.

break;
case SPA_PARAM_Props:;
_Static_assert(SPA_PARAM_Props == 2, "wrong SPA_PARAM_Props");
/* TODO: reconfigure the stream according to the new properties */
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I honestly do not know, hence the TODO.

Copy link
Member

@marmarek marmarek left a comment

Choose a reason for hiding this comment

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

Some packaging comments

@DemiMarie DemiMarie requested a review from marmarek July 28, 2022 20:50
@marmarek
Copy link
Member

marmarek commented Jul 31, 2022

Some quick test results (this PR + stock pacat-simple-vchan):

  • playback seems to work, both via pipewire natively and via pipewire-pulseaudio
  • recording seems to work with pipewire natively, but is very choppy via pipewire-pulseaudio
  • the module seems to properly sent record start/stop commands
  • the module seems to not send playback start/stop commands, instead it sends silence (zeroes) when nothing is playing -> this prevents the actual device to suspend when unused, and results in constant CPU usage on the pacat-simple-vchan side

@marmarek
Copy link
Member

  • when pipewire is started first, then some client (trying to) play something and only then pacat-simple-vchan, then log has endless stream of Overrun: asled tp wrote 2048 bytes, but can only write 0
  • when recording is started in VM first, but recording is allowed later, there is a stream of Underrun: asked to read 2048 bytes, but only xx available (where xx is mostly non-zero, but sometimes 0). At one case two cases, when I stopped recording in the VM (closed the client), then pipewire crashed with SIGABRT and corrupted size vs prev_size message (I have a backtrace and coredump, but I'm not sure how useful they will be in practice)

In both of the above, the client is really connected via pipewire-pulseaudio (pavucontrol and/or firefox playing something)

@DemiMarie
Copy link
Contributor Author

DemiMarie commented Aug 1, 2022

Some quick test results (this PR + stock pacat-simple-vchan):

  • recording seems to work with pipewire natively, but is very choppy via pipewire-pulseaudio

Will need to debug this. Does changing the max quantum in 30_qubes.conf help?

  • the module seems to not send playback start/stop commands, instead it sends silence (zeroes) when nothing is playing -> this prevents the actual device to suspend when unused, and results in constant CPU usage on the pacat-simple-vchan side

I am pretty sure that these do get sent. What does not get sent is an initial cork command at startup. I can add that easily, but, pavucontrol seems to keep an audio stream open all the time. libpipewire-module-qubes.so is just relaying the events PipeWire provides it, so I think this part is a bug in either PipeWire or pavucontrol.

  • when pipewire is started first, then some client (trying to) play something and only then pacat-simple-vchan, then log has endless stream of Overrun: asled tp wrote 2048 bytes, but can only write 0

I have not managed to reproduce this yet.

  • when recording is started in VM first, but recording is allowed later, there is a stream of Underrun: asked to read 2048 bytes, but only xx available (where xx is mostly non-zero, but sometimes 0). At one case two cases, when I stopped recording in the VM (closed the client), then pipewire crashed with SIGABRT and corrupted size vs prev_size message (I have a backtrace and coredump, but I'm not sure how useful they will be in practice)

Please send both to me privately.

@DemiMarie
Copy link
Contributor Author

DemiMarie commented Aug 1, 2022

  • the module seems to not send playback start/stop commands, instead it sends silence (zeroes) when nothing is playing -> this prevents the actual device to suspend when unused, and results in constant CPU usage on the pacat-simple-vchan side

Fixed by reporting the current stream state on all vchan connections.

  • when pipewire is started first, then some client (trying to) play something and only then pacat-simple-vchan, then log has endless stream of Overrun: asled tp wrote 2048 bytes, but can only write 0

Hopefully this is fixed as well.

@DemiMarie
Copy link
Contributor Author

DemiMarie commented Aug 1, 2022

  • when recording is started in VM first, but recording is allowed later, there is a stream of Underrun: asked to read 2048 bytes, but only xx available (where xx is mostly non-zero, but sometimes 0). At one case two cases, when I stopped recording in the VM (closed the client), then pipewire crashed with SIGABRT and corrupted size vs prev_size message (I have a backtrace and coredump, but I'm not sure how useful they will be in practice)

I get occasional underruns, but they stop after a few seconds. No crashes so far.

@marmarek
Copy link
Member

marmarek commented Aug 1, 2022

CI:

qubes-pw-module.c: In function 'playback_stream_process':
qubes-pw-module.c:643:22: error: unused variable 'control_vchan' [-Werror=unused-variable]
  643 |     struct libvchan *control_vchan = capture_stream->vchan;
      |                      ^~~~~~~~~~~~~

@marmarek
Copy link
Member

marmarek commented Aug 1, 2022

Delay acknowledgement of incoming events

(I see you reverted already, but I'll comment anyway)
If you watch vchan with epoll(() or similar, you need to call libvchan_wait() when processing an event, before any other libvchan_* function. Otherwise you may miss some events (because libvchan_wait() might acknowledge some later state change, so it won't be reported by epoll()).

But changing the order on its own should not cause SEGV - if that happens, you either called libvchan_close() in between, or there is some other bug.

@DemiMarie
Copy link
Contributor Author

PipelineRetryFailed

Several keys were missing.
Just use "qubes-audio".
pacat-simple-vchan needs to know the current state of the streams
upon vchan connection.  Otherwise, the playback stream will not be
corked and pacat-simple-vchan will consume CPU for no good reason.

Reported-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Assertions are not an error handling mechanism.  If creating a vchan
fails, or if the vchan cannot be registered, do something other than
asserting.
xruns are a warning, not an error.
This caused infrequent reconnection failures.
If a stream state change has been requested, immediately after vchan
connection is a good time to honor that request.
An overrun could be due to a lost uncork command.  Send a fresh uncork
command in that case.
Not having a default is unnecessarily annoying, and setting the size in
the configuration file makes overriding it more difficult than it would
be otherwise.

Suggested-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
This causes a SIGSEGV; will probably revert.
This is necessary to support loading the module multiple times.
Even a dead stream needs to be shut down properly.
stream->impl can be NULL in this case.
It turns out to only need 320 bytes.
The only time this can fail is if SPA buffers are too small.  If this
happens, it should be caught during testing.
Suggested-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Wim Taymans's line cannot be changed, but mine can.
INT_MAX/2 (0x3FFFFFFF) should be enough for everyone :).  This avoids a
Xen-specific limit.
struct impl is shared by multiple threads simultaneously, and updates to
a bitfield cannot be made atomic.  Better to use ordinary boolean values
in case something needs to be made atomic in the future.
Otherwise we will block the realtime thread.
Otherwise the two could conflict.
This allows it to be managed by Qubes.
This ensures that 'make install' works if run twice.
It makes no sense to _not_ have the PipeWire agent depend on PipeWire,
as without PipeWire it is useless.
The PipeWire agent is actually slightly more code than the PulseAudio
agent, but the benefits more than outweigh this.
@marmarek marmarek merged commit 089e62d into QubesOS:master Apr 26, 2023
@DemiMarie DemiMarie deleted the pipewire branch April 26, 2023 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pipewire audio support

3 participants