Skip to content
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

Doesn't work with pipewire #26

Closed
Ella-0 opened this issue May 23, 2021 · 25 comments · May be fixed by #69 or #71
Closed

Doesn't work with pipewire #26

Ella-0 opened this issue May 23, 2021 · 25 comments · May be fixed by #69 or #71

Comments

@Ella-0
Copy link

Ella-0 commented May 23, 2021

Not exactly sure what the issue is with pipewire; initial device enumeration here works but later down the line they all get rejected.

@illiliti
Copy link
Owner

Briefly looking at source, i guess the problem is here:

https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/spa/plugins/alsa/alsa-udev.c#L144

Try to remove that check.

@Ella-0
Copy link
Author

Ella-0 commented May 23, 2021

that doesn't change much unfortunately (though I think removing it is still required) however looking back at the initial enumerate function it produces different results with actual udev and udev-zero. I extracted the following example code:

#include <stdio.h>
#include <libudev.h>

int main() {
	struct udev *udev = udev_new();
	struct udev_enumerate *enumerate = udev_enumerate_new(udev);

	enumerate = udev_enumerate_new(udev);

	udev_enumerate_add_match_subsystem(enumerate, "sound");
	udev_enumerate_scan_devices(enumerate);
	
	for (struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate); devices;
		devices = udev_list_entry_get_next(devices)) {
		puts(udev_list_entry_get_name(devices));

		struct udev_device *dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(devices));

		udev_device_unref(dev);

	}
	udev_enumerate_unref(enumerate);
}

an I ran with ./a.out | sort
with udev i get the following

/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/controlC1
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/hwC1D0
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D3p
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D7p
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D8p
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D9p
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/sound/card2
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/sound/card2/controlC2
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/sound/card2/pcmC2D0p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/controlC0
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/hwC0D0
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/hwC0D2
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D0c
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D0p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D10p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D3p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D7p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D8p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D9p
/sys/devices/virtual/sound/seq
/sys/devices/virtual/sound/timer

with libudev-zero i get

/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/controlC1
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/hwC1D0
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D3p
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D7p
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D8p
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/pcmC1D9p
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/sound/card2/controlC2
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/sound/card2/pcmC2D0p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/controlC0
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/hwC0D0
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/hwC0D2
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D0c
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D0p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D10p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D3p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D7p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D8p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D9p
/sys/devices/virtual/sound/seq
/sys/devices/virtual/sound/timer

libudev-zero seems not to be including the following which is what pipewire is looking for:

/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/sound/card2
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0

@illiliti
Copy link
Owner

Quick workaround(or real fix???)

diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
index e7a9e8c8..d4c81448 100644
--- a/spa/plugins/alsa/alsa-udev.c
+++ b/spa/plugins/alsa/alsa-udev.c
@@ -141,9 +141,6 @@ static uint32_t get_card_id(struct impl *this, struct udev_device *dev)
 	if ((str = udev_device_get_property_value(dev, "SOUND_CLASS")) && spa_streq(str, "modem"))
 		return SPA_ID_INVALID;
 
-	if ((str = udev_device_get_property_value(dev, "SOUND_INITIALIZED")) == NULL)
-		return SPA_ID_INVALID;
-
 	if ((str = udev_device_get_property_value(dev, "DEVPATH")) == NULL)
 		return SPA_ID_INVALID;
 
@@ -618,7 +615,7 @@ static int enum_devices(struct impl *this)
 
 	for (devices = udev_enumerate_get_list_entry(enumerate); devices;
 			devices = udev_list_entry_get_next(devices)) {
-		struct udev_device *dev;
+		struct udev_device *dev, *pdev;
 
 		dev = udev_device_new_from_syspath(this->udev, udev_list_entry_get_name(devices));
 		if (dev == NULL)
@@ -626,6 +623,13 @@ static int enum_devices(struct impl *this)
 
 		process_device(this, ACTION_ADD, dev);
 
+		pdev = udev_device_get_parent(dev)
+		if (pdev) {
+		    process_device(this, ACTION_ADD, pdev);
+		}
+
+		/* no need to call udev_device_unref(pdev) here.
+		   udev_device_unref() will free parent device implicitly */
 		udev_device_unref(dev);
 	}
 	udev_enumerate_unref(enumerate);

@Ella-0
Copy link
Author

Ella-0 commented May 24, 2021

This was successful in discovering devices (tested with spa-monitor /usr/lib/spa-0.2/alsa/libspa-alsa.so) weirdly pipewire is still not working but I think that's an issue with musl 😕 (the alsa spa module segfaults if not built in debug mode)

@illiliti
Copy link
Owner

By the way, pipewire can be compiled without udev support -> https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/meson_options.txt#L168-171

@Ella-0
Copy link
Author

Ella-0 commented May 24, 2021

Unfortunately this option isn't respect by the build system and it tries to build stuff requiring udev anyway.

@capezotte
Copy link

capezotte commented Dec 5, 2021

Just a heads-up, the ((str = ...)) asssignment was rewritten and broke the patch mentioned in this issue.

For newer versions of Pipewire, a slightly different one is needed:

diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
index e7a9e8c8..d4c81448 100644
--- a/spa/plugins/alsa/alsa-udev.c
+++ b/spa/plugins/alsa/alsa-udev.c
@@ -141,9 +141,6 @@ static uint32_t get_card_id(struct impl *this, struct udev_device *dev)
 	if ((str = udev_device_get_property_value(dev, "SOUND_CLASS")) && spa_streq(str, "modem"))
 		return SPA_ID_INVALID;
 
-	if (udev_device_get_property_value(dev, "SOUND_INITIALIZED") == NULL)
-		return SPA_ID_INVALID;
-
 	if ((str = udev_device_get_property_value(dev, "DEVPATH")) == NULL)
 		return SPA_ID_INVALID;
 
@@ -618,7 +615,7 @@ static int enum_devices(struct impl *this)
 
 	for (devices = udev_enumerate_get_list_entry(enumerate); devices;
 			devices = udev_list_entry_get_next(devices)) {
-		struct udev_device *dev;
+		struct udev_device *dev, *pdev;
 
 		dev = udev_device_new_from_syspath(this->udev, udev_list_entry_get_name(devices));
 		if (dev == NULL)
@@ -626,6 +623,13 @@ static int enum_devices(struct impl *this)
 
 		process_device(this, ACTION_ADD, dev);
 
+		pdev = udev_device_get_parent(dev);
+		if (pdev) {
+		    process_device(this, ACTION_ADD, pdev);
+		}
+
+		/* no need to call udev_device_unref(pdev) here.
+		   udev_device_unref() will free parent device implicitly */
 		udev_device_unref(dev);
 	}
 	udev_enumerate_unref(enumerate);

@folliehiyuki
Copy link

@illiliti @capezotte is there any reason this patch hasn't been sent to upstream?

I just tried out libudev-zero on Alpine Linux for the 1st time, and apparently pipewire doesn't like to work (I can't see any sinks / sources). The original work around 4510b27 doesn't seem to have any effects anymore.

I'm running pipewire version 0.3.51 (with pipewire-pulse and pipewire-jack) and libudev-zero version 1.0.1

@illiliti
Copy link
Owner

Yeah, getting this patch upstream is probably worth the effort, but personally i'm not going to do that because

  1. I don't use pipewire, i can't test it.
  2. We are ugly ones, you know. I know it's a bit too early to say that, but it's highly likely that pipewire would just reject this patch due to its specificity to libudev-zero.
  3. I can't guarantee that they won't break libudev-zero again. Constant fighting with upstream regarding udev stuff is a last thing i want to do.

Anyway, feel free to take the flag and submit this patch to pipewire.

@capezotte
Copy link

I have a similar setup – Gentoo with musl, libudev-zero git master, systemwide PipeWire 0.3.51 (with my version of the patch) replacing Jack and Pulse – and audio works.

Removing that check seems very likely to break something for the majority using udev, so I haven't considered upstreaming it.

@folliehiyuki
Copy link

folliehiyuki commented May 23, 2022

Maybe I should ask upstream once, so they at least know about the patch and that we 'ugly ones' exist ✌️

For now I'll just ask for review and the inclusion of this patch into Alpine Linux.

@folliehiyuki
Copy link

Issue sent to upstream: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/2398

@capezotte
Copy link

Version 1.0 came around and renamed some functions and variables, breaking the patch.

Patch v3:

diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
index 58ff5032d..b0b7a945d 100644
--- a/spa/plugins/alsa/alsa-udev.c
+++ b/spa/plugins/alsa/alsa-udev.c
@@ -162,8 +162,6 @@ static unsigned int get_card_nr(struct impl *this, struct udev_device *udev_devi
 	if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && spa_streq(str, "modem"))
 		return SPA_ID_INVALID;
 
-	if (udev_device_get_property_value(udev_device, "SOUND_INITIALIZED") == NULL)
-		return SPA_ID_INVALID;
 
 	if ((str = udev_device_get_property_value(udev_device, "DEVPATH")) == NULL)
 		return SPA_ID_INVALID;
@@ -982,7 +982,7 @@ static int enum_cards(struct impl *this)
 
 	for (udev_devices = udev_enumerate_get_list_entry(enumerate); udev_devices;
 			udev_devices = udev_list_entry_get_next(udev_devices)) {
-		struct udev_device *udev_device;
+		struct udev_device *udev_device, *pdev;
 
 		udev_device = udev_device_new_from_syspath(this->udev,
 		                                           udev_list_entry_get_name(udev_devices));
@@ -991,6 +991,8 @@ static int enum_cards(struct impl *this)
 
 		process_card(this, ACTION_ADD, udev_device);
 
+		if ((pdev = udev_device_get_parent(udev_device)))
+			process_card(this, ACTION_ADD, pdev);
 		udev_device_unref(udev_device);
 	}
 	udev_enumerate_unref(enumerate);

@xplshn
Copy link

xplshn commented Apr 14, 2024

@illiliti Given that you have a Gitlab account (I don't), could you make a PR in the pipewire repo instead of an issue? There are around 780~ open issues, and I think that's why no core developer has looked at the proposed patch, however, there are only 27 open PRs at the time of writing, so I think it'd make sense to open a PR instead. Thanks

:)

@capezotte
Copy link

capezotte commented Apr 14, 2024

They did look into the issue; however, they'd rather the SOUND_INITIALIZED property got implemented in libudev-zero, since ignoring it causes race conditions when hotplugging complex sound cards.

I tried writing a patch doing just that for this project but I don't have enough C skills to make a implementation of it that doesn't segfault. It seems simple: just check if the sound card sysfs has a controlC[0-9] file.

@xplshn
Copy link

xplshn commented Apr 14, 2024

Hmm. Maybe the correct solution is packaging a separate pipewire for us Alpine users that do have libudev-zero in our repos. Happens that I don't like the build system that Alpine uses, its crappy, slow, and relies on custom templates that work like shell scripts but worse.

@xplshn
Copy link

xplshn commented Apr 14, 2024

(correct solution for now... Obviously libeudev-zero should implement that if we hope to replace udev and have working systems).

@sertonix
Copy link
Contributor

(they are just shell scripts)

@xplshn
Copy link

xplshn commented Apr 14, 2024

@illiliti Couldn't we implement this using XDG_RUNTIME_DIR if it is present or an alternative dir to implement this logic? Since it requires saving data.

@capezotte
Copy link

@xplshn @folliehiyuki can you try the PR I just sent?

Tested on:

@asimovc
Copy link

asimovc commented Jun 28, 2024

This patch doesn't work anymore, need to be updated

@gordon-quad
Copy link

ACTION_ADD needs to be replaced with ACTION_CHANGE and process_card with process_udev_device and it works again.

diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
index 9420401f0..4e0f751cf 100644
--- a/spa/plugins/alsa/alsa-udev.c
+++ b/spa/plugins/alsa/alsa-udev.c
@@ -164,9 +164,6 @@ static unsigned int get_card_nr(struct impl *this, struct udev_device *udev_devi
 	if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && spa_streq(str, "modem"))
 		return SPA_ID_INVALID;
 
-	if (udev_device_get_property_value(udev_device, "SOUND_INITIALIZED") == NULL)
-		return SPA_ID_INVALID;
-
 	if ((str = udev_device_get_property_value(udev_device, "DEVPATH")) == NULL)
 		return SPA_ID_INVALID;
 
@@ -970,7 +967,7 @@ static int enum_cards(struct impl *this)
 
 	for (udev_devices = udev_enumerate_get_list_entry(enumerate); udev_devices;
 			udev_devices = udev_list_entry_get_next(udev_devices)) {
-		struct udev_device *udev_device;
+		struct udev_device *udev_device, *udev_parent_device;
 
 		udev_device = udev_device_new_from_syspath(this->udev,
 		                                           udev_list_entry_get_name(udev_devices));
@@ -979,6 +976,13 @@ static int enum_cards(struct impl *this)
 
 		process_udev_device(this, ACTION_CHANGE, udev_device);
 
+		udev_parent_device = udev_device_get_parent(udev_device);
+		if (udev_parent_device) {
+			process_udev_device(this, ACTION_CHANGE, udev_parent_device);
+		}
+
+		/* no need to call udev_device_unref(udev_parent_device) here.
+		   udev_device_unref() will free parent device implicitly */
 		udev_device_unref(udev_device);
 	}
 	udev_enumerate_unref(enumerate);

Will try my best to keep this patch updated.

@asimovc
Copy link

asimovc commented Jun 30, 2024

@gordon-quad i tried the patch with 1.2.0 and get this

patching file spa/plugins/alsa/alsa-udev.c
Hunk 2 FAILED 915/912.
 
 	start_inotify(this);
 
-	if (spa_streq(action, "change")) {
+	if (spa_streq(action, "change") || spa_streq(action, "add")) {
 		process_card(this, ACTION_ADD, udev_device);
 	} else if (spa_streq(action, "remove")) {
 		process_card(this, ACTION_REMOVE, udev_device);

@gordon-quad
Copy link

gordon-quad commented Jun 30, 2024

@asimovc can you double-check what are you patching with? I don't see any mentions of the hunk you presented nor the line number in my patch...

I used git to format my patch, but double-checked with sources downloaded as a tarball and it applied successfully

$ wget -qc https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.2.0/pipewire-1.2.0.tar.bz2
$ tar -xpf pipewire-1.2.0.tar.bz2
$ cd pipewire-1.2.0
$ cat /tmp/pipewire-udev.patch | patch --verbose -p1
Hmm...  Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
|index 9420401f0..4e0f751cf 100644
|--- a/spa/plugins/alsa/alsa-udev.c
|+++ b/spa/plugins/alsa/alsa-udev.c
--------------------------
patching file spa/plugins/alsa/alsa-udev.c
Using Plan A...
Hunk #1 succeeded at 164.
Hunk #2 succeeded at 967.
Hunk #3 succeeded at 976.
done
$ sha256sum /tmp/pipewire-udev.patch
aa158445fb5cbee2a6f1527da283c8ecab226a03fe33dc109d5e8b7895d4734e  /tmp/pipewire-udev.patch

@asimovc
Copy link

asimovc commented Jun 30, 2024

I revised and now worked, my fault. Thanks bro

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
8 participants