|
20 | 20 |
|
21 | 21 | #include "pipewire-audio.h" |
22 | 22 |
|
| 23 | +#include <spa/debug/types.h> |
| 24 | + |
23 | 25 | #include <util/dstr.h> |
24 | 26 |
|
25 | 27 | /* Source for capturing applciation audio using PipeWire */ |
@@ -549,52 +551,78 @@ static void destroy_capture_sink(struct obs_pw_audio_capture_app *pwac) |
549 | 551 | /* ------------------------------------------------- */ |
550 | 552 |
|
551 | 553 | /* Default system sink */ |
552 | | -static void on_default_sink_info_cb(void *data, const struct pw_node_info *info) |
| 554 | +static void on_default_sink_param_cb(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, |
| 555 | + const struct spa_pod *param) |
553 | 556 | { |
554 | | - if ((info->change_mask & PW_NODE_CHANGE_MASK_PROPS) == 0 || !info->props || !info->props->n_items) { |
| 557 | + UNUSED_PARAMETER(seq); |
| 558 | + UNUSED_PARAMETER(index); |
| 559 | + UNUSED_PARAMETER(next); |
| 560 | + |
| 561 | + if (id != SPA_PARAM_EnumFormat) { |
555 | 562 | return; |
556 | 563 | } |
557 | 564 |
|
558 | 565 | struct obs_pw_audio_capture_app *pwac = data; |
559 | 566 |
|
560 | | - /** Use stereo if |
561 | | - * - The default sink uses the Pro Audio profile, since all streams will be configured to use stereo |
562 | | - * https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#what-is-the-pro-audio-profile |
563 | | - * - The default sink doesn't have the needed props and there isn't already an app capture sink */ |
| 567 | + uint32_t media_type = 0, parsed_id = 0, channels = 0; |
| 568 | + struct spa_pod *position_pod = NULL; |
564 | 569 |
|
565 | | - const char *channels = spa_dict_lookup(info->props, PW_KEY_AUDIO_CHANNELS); |
566 | | - const char *position = spa_dict_lookup(info->props, SPA_KEY_AUDIO_POSITION); |
567 | | - if (!channels || !position) { |
568 | | - if (pwac->sink.proxy) { |
569 | | - return; |
| 570 | + struct spa_pod_parser p; |
| 571 | + spa_pod_parser_pod(&p, param); |
| 572 | + |
| 573 | + spa_pod_parser_get_object(&p, SPA_TYPE_OBJECT_Format, &parsed_id, SPA_FORMAT_mediaType, SPA_POD_Id(&media_type), |
| 574 | + SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&channels), SPA_FORMAT_AUDIO_position, |
| 575 | + SPA_POD_OPT_Pod(&position_pod)); |
| 576 | + |
| 577 | + if (parsed_id != SPA_PARAM_EnumFormat || media_type != SPA_MEDIA_TYPE_audio || !channels || !position_pod) { |
| 578 | + goto stereo_fallback; |
| 579 | + } |
| 580 | + |
| 581 | + uint32_t position_n = 0; |
| 582 | + uint32_t *position_arr = spa_pod_get_array(position_pod, &position_n); |
| 583 | + |
| 584 | + struct dstr position_str; |
| 585 | + dstr_init(&position_str); |
| 586 | + |
| 587 | + for (size_t i = 0; i < position_n; i++) { |
| 588 | + const char *chn = spa_debug_type_find_short_name(spa_type_audio_channel, position_arr[i]); |
| 589 | + |
| 590 | + if (strstr(chn, "AUX") != NULL) { |
| 591 | + // Sink is configured for pro audio, use stereo |
| 592 | + // https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#what-is-the-pro-audio-profile |
| 593 | + dstr_copy(&position_str, "FL,FR"); |
| 594 | + break; |
| 595 | + } |
| 596 | + |
| 597 | + dstr_cat(&position_str, chn); |
| 598 | + |
| 599 | + if (position_n - 1 != i) { |
| 600 | + dstr_cat_ch(&position_str, ','); |
570 | 601 | } |
571 | | - channels = "2"; |
572 | | - position = "FL,FR"; |
573 | | - } else if (astrstri(position, "AUX")) { |
574 | | - /* Pro Audio sinks use AUX0,AUX1... and so on as their position (see link above) */ |
575 | | - channels = "2"; |
576 | | - position = "FL,FR"; |
577 | 602 | } |
578 | 603 |
|
579 | | - uint32_t c = strtoul(channels, NULL, 10); |
580 | | - if (!c) { |
581 | | - return; |
| 604 | + if (channels != pwac->sink.channels || dstr_cmpi(&position_str, pwac->sink.position.array) != 0) { |
| 605 | + destroy_capture_sink(pwac); |
| 606 | + make_capture_sink(pwac, channels, position_str.array); |
582 | 607 | } |
583 | 608 |
|
584 | | - /* No need to create a new capture sink if the channels are the same */ |
585 | | - if (pwac->sink.channels == c && !dstr_is_empty(&pwac->sink.position) && |
586 | | - dstr_cmp(&pwac->sink.position, position) == 0) { |
| 609 | + dstr_free(&position_str); |
| 610 | + return; |
| 611 | + |
| 612 | +stereo_fallback: |
| 613 | + if (pwac->sink.proxy) { |
587 | 614 | return; |
588 | 615 | } |
589 | 616 |
|
590 | | - destroy_capture_sink(pwac); |
| 617 | + blog(LOG_WARNING, "[pipewire-audio] Could not parse format of default sink. Falling back to stereo."); |
591 | 618 |
|
592 | | - make_capture_sink(pwac, c, position); |
| 619 | + destroy_capture_sink(pwac); |
| 620 | + make_capture_sink(pwac, 2, "[FL,FR]"); |
593 | 621 | } |
594 | 622 |
|
595 | 623 | static const struct pw_node_events default_sink_events = { |
596 | 624 | PW_VERSION_NODE_EVENTS, |
597 | | - .info = on_default_sink_info_cb, |
| 625 | + .param = on_default_sink_param_cb, |
598 | 626 | }; |
599 | 627 |
|
600 | 628 | static void on_default_sink_proxy_removed_cb(void *data) |
@@ -661,6 +689,8 @@ static void default_node_cb(void *data, const char *name) |
661 | 689 | pwac); |
662 | 690 | pw_proxy_add_listener(pwac->default_sink.proxy, &pwac->default_sink.proxy_listener, &default_sink_proxy_events, |
663 | 691 | pwac); |
| 692 | + |
| 693 | + pw_node_subscribe_params((struct pw_node *)pwac->default_sink.proxy, (uint32_t[]){SPA_PARAM_EnumFormat}, 1); |
664 | 694 | } |
665 | 695 | /* ------------------------------------------------- */ |
666 | 696 |
|
|
0 commit comments