diff --git a/0001-bluez5-include-a2dp-codec-profiles-in-route-profiles.patch b/0001-bluez5-include-a2dp-codec-profiles-in-route-profiles.patch new file mode 100644 index 0000000..d1f65c3 --- /dev/null +++ b/0001-bluez5-include-a2dp-codec-profiles-in-route-profiles.patch @@ -0,0 +1,40 @@ +From df1dbee687c819095a6fddce3b24943f9ac47dbc Mon Sep 17 00:00:00 2001 +From: Pauli Virtanen +Date: Sun, 14 Feb 2021 14:27:58 +0200 +Subject: [PATCH 01/30] bluez5: include a2dp codec profiles in route profiles + +--- + spa/plugins/bluez5/bluez5-device.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c +index 84c1fa6c2..9e42d9d99 100644 +--- a/spa/plugins/bluez5/bluez5-device.c ++++ b/spa/plugins/bluez5/bluez5-device.c +@@ -666,8 +666,9 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, + const char *name_prefix, *description, *port_type; + enum spa_param_availability available; + enum spa_bt_form_factor ff; ++ const struct a2dp_codec *codec; + char name[128]; +- uint32_t i, mask; ++ uint32_t i, j, mask; + + ff = spa_bt_form_factor_from_class(device->bluetooth_class); + +@@ -769,10 +770,9 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, + spa_pod_builder_pop(b, &f[1]); + spa_pod_builder_prop(b, SPA_PARAM_ROUTE_profiles, 0); + spa_pod_builder_push_array(b, &f[1]); +- for (i = 1; i < 3; i++) { +- if (profile_direction_mask(this, i) & (1 << direction)) ++ for (i = 1; (j = get_profile_from_index(this, i, &codec)) != SPA_ID_INVALID; i++) ++ if (profile_direction_mask(this, j) & (1 << direction)) + spa_pod_builder_int(b, i); +- } + spa_pod_builder_pop(b, &f[1]); + + if (dev != SPA_ID_INVALID) { +-- +2.26.2 + diff --git a/0005-fix-some-warnings.patch b/0005-fix-some-warnings.patch new file mode 100644 index 0000000..56987bf --- /dev/null +++ b/0005-fix-some-warnings.patch @@ -0,0 +1,23 @@ +From 63a3811aa76802e9585e04cf12bf8de617442105 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Thu, 18 Feb 2021 12:42:06 +0100 +Subject: [PATCH 05/30] fix some warnings + +--- + spa/plugins/bluez5/backend-native.c | 2 +- + src/examples/media-session/default-routes.c | 18 +++++++++++++----- + 2 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c +index 4df714088..b4d4e5edf 100644 +--- a/spa/plugins/bluez5/backend-native.c ++++ b/spa/plugins/bluez5/backend-native.c +@@ -437,7 +437,7 @@ static bool rfcomm_hfp_hf(struct spa_source *source, char* buf) + struct rfcomm *rfcomm = source->data; + struct spa_bt_backend *backend = rfcomm->backend; + unsigned int features; +- unsigned int gain; ++ unsigned int SPA_UNUSED gain; + unsigned int selected_codec; + char* token; + char separators[] = "\r\n:"; diff --git a/0006-spa-escape-double-quotes.patch b/0006-spa-escape-double-quotes.patch new file mode 100644 index 0000000..1727a84 --- /dev/null +++ b/0006-spa-escape-double-quotes.patch @@ -0,0 +1,42 @@ +From 6a219092818a7a7e357441256d8070ae77ac2d3a Mon Sep 17 00:00:00 2001 +From: eater <=@eater.me> +Date: Thu, 18 Feb 2021 13:59:09 +0100 +Subject: [PATCH 06/30] spa: escape double quotes + +--- + spa/include/spa/utils/json.h | 3 +++ + spa/tests/test-json.c | 4 ++-- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/spa/include/spa/utils/json.h b/spa/include/spa/utils/json.h +index 01ff0d567..de2cce6fa 100644 +--- a/spa/include/spa/utils/json.h ++++ b/spa/include/spa/utils/json.h +@@ -346,6 +346,9 @@ static inline int spa_json_encode_string(char *str, int size, const char *val) + case '\f': + __PUT('\\'); __PUT('f'); + break; ++ case '"': ++ __PUT('\\'); __PUT('"'); ++ break; + default: + __PUT(*val); + break; +diff --git a/spa/tests/test-json.c b/spa/tests/test-json.c +index dd510841f..c2bb5f815 100644 +--- a/spa/tests/test-json.c ++++ b/spa/tests/test-json.c +@@ -160,8 +160,8 @@ static void test_encode(void) + spa_assert(strncmp(dst4, "\"tes", 4) == 0); + spa_assert(spa_json_encode_string(dst6, sizeof(dst6), "test") == 6); + spa_assert(strncmp(dst6, "\"test\"", 6) == 0); +- spa_assert(spa_json_encode_string(dst, sizeof(dst), "test\"\n\r \t\b\f\'") == 19); +- spa_assert(strcmp(dst, "\"test\"\\n\\r \\t\\b\\f'\"") == 0); ++ spa_assert(spa_json_encode_string(dst, sizeof(dst), "test\"\n\r \t\b\f\'") == 20); ++ spa_assert(strcmp(dst, "\"test\\\"\\n\\r \\t\\b\\f'\"") == 0); + } + + int main(int argc, char *argv[]) +-- +2.26.2 + diff --git a/0009-bluez5-volumes-need-to-be-distributed-to-all-channel.patch b/0009-bluez5-volumes-need-to-be-distributed-to-all-channel.patch new file mode 100644 index 0000000..106cdca --- /dev/null +++ b/0009-bluez5-volumes-need-to-be-distributed-to-all-channel.patch @@ -0,0 +1,37 @@ +From 8c5ca000ef7fddd9be9cd2211d880736e97b956d Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Thu, 18 Feb 2021 17:47:22 +0100 +Subject: [PATCH 09/30] bluez5: volumes need to be distributed to all channels + +Otherwise some channels might become silent. + +See #741 +--- + spa/plugins/bluez5/bluez5-device.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c +index 4698efb92..be7064941 100644 +--- a/spa/plugins/bluez5/bluez5-device.c ++++ b/spa/plugins/bluez5/bluez5-device.c +@@ -923,11 +923,15 @@ static int node_set_volume(struct impl *this, struct node *node, float volumes[] + uint8_t buffer[4096]; + struct spa_pod_builder b = { 0 }; + struct spa_pod_frame f[1]; ++ uint32_t i; ++ ++ if (n_volumes == 0) ++ return -EINVAL; + + spa_log_info(this->log, "node %p volume %f", node, volumes[0]); + +- node->n_channels = n_volumes; +- memcpy(node->volumes, volumes, sizeof(float) * SPA_AUDIO_MAX_CHANNELS); ++ for (i = 0; i < node->n_channels; i++) ++ node->volumes[i] = volumes[i % n_volumes]; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + spa_pod_builder_push_object(&b, &f[0], +-- +2.26.2 + diff --git a/0011-bluez5-backend-native-Check-volume-values.patch b/0011-bluez5-backend-native-Check-volume-values.patch new file mode 100644 index 0000000..4f2c038 --- /dev/null +++ b/0011-bluez5-backend-native-Check-volume-values.patch @@ -0,0 +1,141 @@ +From 7a5a94470453e0cbd3a087b1be2aa8960ff43c07 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Danis?= +Date: Wed, 17 Feb 2021 18:41:24 +0100 +Subject: [PATCH 11/30] bluez5: backend-native: Check volume values + +The value from +VGM/+VGS AT commands or events should be between 0 than 15. +--- + spa/plugins/bluez5/backend-native.c | 68 +++++++++++++++++++++++------ + 1 file changed, 55 insertions(+), 13 deletions(-) + +diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c +index b4d4e5edf..2979a0f2e 100644 +--- a/spa/plugins/bluez5/backend-native.c ++++ b/spa/plugins/bluez5/backend-native.c +@@ -199,6 +199,8 @@ static void rfcomm_send_reply(struct spa_source *source, char *data) + #ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE + static bool rfcomm_hsp_ag(struct spa_source *source, char* buf) + { ++ struct rfcomm *rfcomm = source->data; ++ struct spa_bt_backend *backend = rfcomm->backend; + unsigned int gain, dummy; + + /* There are only three HSP AT commands: +@@ -206,11 +208,21 @@ static bool rfcomm_hsp_ag(struct spa_source *source, char* buf) + * AT+VGM=value: value between 0 and 15, sent by the HS to AG to set the microphone gain. + * AT+CKPD=200: Sent by HS when headset button is pressed. */ + if (sscanf(buf, "AT+VGS=%d", &gain) == 1) { +- /* t->speaker_gain = gain; */ +- rfcomm_send_reply(source, "OK"); ++ if (gain <= 15) { ++ /* t->speaker_gain = gain; */ ++ rfcomm_send_reply(source, "OK"); ++ } else { ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGS gain: %s", buf); ++ rfcomm_send_reply(source, "ERROR"); ++ } + } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1) { +- /* t->microphone_gain = gain; */ +- rfcomm_send_reply(source, "OK"); ++ if (gain <= 15) { ++ /* t->microphone_gain = gain; */ ++ rfcomm_send_reply(source, "OK"); ++ } else { ++ rfcomm_send_reply(source, "ERROR"); ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGM gain: %s", buf); ++ } + } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) { + rfcomm_send_reply(source, "OK"); + } else { +@@ -222,6 +234,8 @@ static bool rfcomm_hsp_ag(struct spa_source *source, char* buf) + + static bool rfcomm_hsp_hs(struct spa_source *source, char* buf) + { ++ struct rfcomm *rfcomm = source->data; ++ struct spa_bt_backend *backend = rfcomm->backend; + unsigned int gain; + + /* There are only three HSP AT result codes: +@@ -232,9 +246,17 @@ static bool rfcomm_hsp_hs(struct spa_source *source, char* buf) + * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because + * it does not expect a reply. */ + if (sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) { +- /* t->microphone_gain = gain; */ ++ if (gain <= 15) { ++ /* t->microphone_gain = gain; */ ++ } else { ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGS gain: %s", buf); ++ } + } else if (sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) { +- /* t->speaker_gain = gain; */ ++ if (gain <= 15) { ++ /* t->speaker_gain = gain; */ ++ } else { ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGM gain: %s", buf); ++ } + } + + return true; +@@ -420,11 +442,21 @@ static bool rfcomm_hfp_ag(struct spa_source *source, char* buf) + } + rfcomm_send_reply(source, "OK"); + } else if (sscanf(buf, "AT+VGM=%u", &gain) == 1) { +- /* t->microphone_gain = gain; */ +- rfcomm_send_reply(source, "OK"); ++ if (gain <= 15) { ++ /* t->microphone_gain = gain; */ ++ rfcomm_send_reply(source, "OK"); ++ } else { ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGM gain: %s", buf); ++ rfcomm_send_reply(source, "ERROR"); ++ } + } else if (sscanf(buf, "AT+VGS=%u", &gain) == 1) { +- /* t->speaker_gain = gain; */ +- rfcomm_send_reply(source, "OK"); ++ if (gain <= 15) { ++ /* t->speaker_gain = gain; */ ++ rfcomm_send_reply(source, "OK"); ++ } else { ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGS gain: %s", buf); ++ rfcomm_send_reply(source, "ERROR"); ++ } + } else { + return false; + } +@@ -437,7 +469,7 @@ static bool rfcomm_hfp_hf(struct spa_source *source, char* buf) + struct rfcomm *rfcomm = source->data; + struct spa_bt_backend *backend = rfcomm->backend; + unsigned int features; +- unsigned int SPA_UNUSED gain; ++ unsigned int gain; + unsigned int selected_codec; + char* token; + char separators[] = "\r\n:"; +@@ -491,12 +523,22 @@ static bool rfcomm_hfp_hf(struct spa_source *source, char* buf) + /* get next token */ + token = strtok(NULL, separators); + gain = atoi(token); +- /* t->speaker_gain = gain; */ ++ ++ if (gain <= 15) { ++ /* t->speaker_gain = gain; */ ++ } else { ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGM gain: %s", token); ++ } + } else if (strncmp(token, "+VGS", 4) == 0) { + /* get next token */ + token = strtok(NULL, separators); + gain = atoi(token); +- /* t->microphone_gain = gain; */ ++ ++ if (gain <= 15) { ++ /* t->microphone_gain = gain; */ ++ } else { ++ spa_log_debug(backend->log, NAME": RFCOMM receive unsupported VGS gain: %s", token); ++ } + } else if (strncmp(token, "OK", 5) == 0) { + switch(rfcomm->hf_state) { + case hfp_hf_brsf: +-- +2.26.2 + diff --git a/0012-media-session-don-t-switch-to-pro-audio-by-default.patch b/0012-media-session-don-t-switch-to-pro-audio-by-default.patch new file mode 100644 index 0000000..f594a83 --- /dev/null +++ b/0012-media-session-don-t-switch-to-pro-audio-by-default.patch @@ -0,0 +1,30 @@ +From 84fc63e60168e2c2f43875423899b4ae3bd89a7b Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Thu, 18 Feb 2021 19:49:34 +0100 +Subject: [PATCH 12/30] media-session: don't switch to pro-audio by default + +Ignore the pro-audio profile when finding the best profile. We only +want to enable the profile when explicitly set. + +Fixes #761 +--- + src/examples/media-session/default-profile.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/examples/media-session/default-profile.c b/src/examples/media-session/default-profile.c +index b17171e63..e311490ea 100644 +--- a/src/examples/media-session/default-profile.c ++++ b/src/examples/media-session/default-profile.c +@@ -176,6 +176,9 @@ static int find_best_profile(struct device *dev, struct profile *pr) + parse_profile(p, &t) < 0) + continue; + ++ if (t.name && strcmp(t.name, "pro-audio") == 0) ++ continue; ++ + if (t.name && strcmp(t.name, "off") == 0) { + off = t; + } +-- +2.26.2 + diff --git a/0013-audioconvert-keep-better-track-of-param-changes.patch b/0013-audioconvert-keep-better-track-of-param-changes.patch new file mode 100644 index 0000000..f22e361 --- /dev/null +++ b/0013-audioconvert-keep-better-track-of-param-changes.patch @@ -0,0 +1,208 @@ +From 44919c83fc51aa5e1a22c903e841897c1b4d274c Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Fri, 19 Feb 2021 09:55:53 +0100 +Subject: [PATCH 13/30] audioconvert: keep better track of param changes + +Keep track of the param changes with the user counter. Make sure to +flip the serial switch whenever a change is pending. Previously +we copied the param from the channelmixer or follower but that +did not always result in a serial change. + +Fixes #764 +--- + spa/plugins/audioconvert/audioadapter.c | 42 +++++++++++++++++-------- + spa/plugins/audioconvert/audioconvert.c | 32 +++++++++++++------ + 2 files changed, 52 insertions(+), 22 deletions(-) + +diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c +index 75b4d1407..5a96c7abb 100644 +--- a/spa/plugins/audioconvert/audioadapter.c ++++ b/spa/plugins/audioconvert/audioadapter.c +@@ -76,6 +76,12 @@ struct impl { + + uint64_t info_all; + struct spa_node_info info; ++#define IDX_EnumFormat 0 ++#define IDX_PropInfo 1 ++#define IDX_Props 2 ++#define IDX_Format 3 ++#define IDX_EnumPortConfig 4 ++#define IDX_PortConfig 5 + struct spa_param_info params[6]; + + struct spa_hook_list hooks; +@@ -206,9 +212,19 @@ static int link_io(struct impl *this) + + static void emit_node_info(struct impl *this, bool full) + { ++ uint32_t i; ++ + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { ++ if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { ++ for (i = 0; i < SPA_N_ELEMENTS(this->params); i++) { ++ if (this->params[i].user > 0) { ++ this->params[i].flags ^= SPA_PARAM_INFO_SERIAL; ++ this->params[i].user = 0; ++ } ++ } ++ } + spa_node_emit_info(&this->hooks, &this->info); + this->info.change_mask = 0; + } +@@ -585,15 +601,15 @@ static void convert_node_info(void *data, const struct spa_node_info *info) + + switch (info->params[i].id) { + case SPA_PARAM_PropInfo: +- idx = 1; ++ idx = IDX_PropInfo; + break; + case SPA_PARAM_Props: +- idx = 2; ++ idx = IDX_Props; + break; + } + if (idx != SPA_ID_INVALID) { +- this->params[idx] = info->params[i]; + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; ++ this->params[idx].user++; + } + } + } +@@ -662,12 +678,12 @@ static void follower_info(void *data, const struct spa_node_info *info) + + switch (info->params[i].id) { + case SPA_PARAM_Props: +- idx = 2; ++ idx = IDX_Props; + break; + } + if (idx != SPA_ID_INVALID) { +- this->params[idx] = info->params[i]; + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; ++ this->params[idx].user++; + } + } + } +@@ -687,12 +703,12 @@ static void follower_port_info(void *data, + + switch (info->params[i].id) { + case SPA_PARAM_Format: +- idx = 3; ++ idx = IDX_Format; + break; + } + if (idx != SPA_ID_INVALID) { +- this->params[idx] = info->params[i]; + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; ++ this->params[idx].user++; + } + } + } +@@ -1166,12 +1182,12 @@ impl_init(const struct spa_handle_factory *factory, + SPA_NODE_FLAG_IN_PORT_CONFIG | + SPA_NODE_FLAG_OUT_PORT_CONFIG | + SPA_NODE_FLAG_NEED_CONFIGURE; +- this->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); +- this->params[1] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); +- this->params[2] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); +- this->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); +- this->params[4] = SPA_PARAM_INFO(SPA_PARAM_EnumPortConfig, SPA_PARAM_INFO_READ); +- this->params[5] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE); ++ this->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); ++ this->params[IDX_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); ++ this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); ++ this->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); ++ this->params[IDX_EnumPortConfig] = SPA_PARAM_INFO(SPA_PARAM_EnumPortConfig, SPA_PARAM_INFO_READ); ++ this->params[IDX_PortConfig] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE); + this->info.params = this->params; + this->info.n_params = 6; + +diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c +index 02cf78302..570db74b2 100644 +--- a/spa/plugins/audioconvert/audioconvert.c ++++ b/spa/plugins/audioconvert/audioconvert.c +@@ -81,7 +81,11 @@ struct impl { + + uint64_t info_all; + struct spa_node_info info; +- struct spa_param_info params[8]; ++#define IDX_EnumPortConfig 0 ++#define IDX_PortConfig 1 ++#define IDX_PropInfo 2 ++#define IDX_Props 3 ++ struct spa_param_info params[4]; + + int n_links; + struct link links[8]; +@@ -121,12 +125,22 @@ struct impl { + + static void emit_node_info(struct impl *this, bool full) + { ++ uint32_t i; ++ + if (this->add_listener) + return; + + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { ++ if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { ++ for (i = 0; i < SPA_N_ELEMENTS(this->params); i++) { ++ if (this->params[i].user > 0) { ++ this->params[i].flags ^= SPA_PARAM_INFO_SERIAL; ++ this->params[i].user = 0; ++ } ++ } ++ } + spa_node_emit_info(&this->hooks, &this->info); + this->info.change_mask = 0; + } +@@ -606,15 +620,15 @@ static void on_channelmix_info(void *data, const struct spa_node_info *info) + + switch (info->params[i].id) { + case SPA_PARAM_PropInfo: +- idx = 2; ++ idx = IDX_PropInfo; + break; + case SPA_PARAM_Props: +- idx = 3; ++ idx = IDX_Props; + break; + } + if (idx != SPA_ID_INVALID) { +- this->params[idx] = info->params[i]; + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; ++ this->params[idx].user++; + } + } + emit_node_info(this, false); +@@ -705,8 +719,8 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m + return res; + + this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS; +- this->params[3].flags ^= SPA_PARAM_INFO_SERIAL; + this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE; ++ this->params[IDX_Props].user++; + } + + /* notify ports of new node */ +@@ -1253,10 +1267,10 @@ impl_init(const struct spa_handle_factory *factory, + SPA_NODE_FLAG_IN_PORT_CONFIG | + SPA_NODE_FLAG_OUT_PORT_CONFIG | + SPA_NODE_FLAG_NEED_CONFIGURE; +- this->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumPortConfig, SPA_PARAM_INFO_READ); +- this->params[1] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE); +- this->params[2] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); +- this->params[3] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); ++ this->params[IDX_EnumPortConfig] = SPA_PARAM_INFO(SPA_PARAM_EnumPortConfig, SPA_PARAM_INFO_READ); ++ this->params[IDX_PortConfig] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE); ++ this->params[IDX_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); ++ this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); + this->info.params = this->params; + this->info.n_params = 4; + +-- +2.26.2 + diff --git a/0018-pulse-server-print-encoding-name-in-format_info.patch b/0018-pulse-server-print-encoding-name-in-format_info.patch new file mode 100644 index 0000000..13ca5dd --- /dev/null +++ b/0018-pulse-server-print-encoding-name-in-format_info.patch @@ -0,0 +1,62 @@ +From 4b591df145afa316e8e047764f9b98fc10cbacd9 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Fri, 19 Feb 2021 14:57:11 +0100 +Subject: [PATCH 18/30] pulse-server: print encoding name in format_info + +--- + src/modules/module-protocol-pulse/format.c | 21 ++++++++++++++++++++- + src/modules/module-protocol-pulse/message.c | 4 +++- + 2 files changed, 23 insertions(+), 2 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c +index 37abe5220..f718e70a7 100644 +--- a/src/modules/module-protocol-pulse/format.c ++++ b/src/modules/module-protocol-pulse/format.c +@@ -476,9 +476,28 @@ enum encoding { + ENCODING_TRUEHD_IEC61937, + ENCODING_DTSHD_IEC61937, + ENCODING_MAX, +- NCODING_INVALID = -1, ++ ENCODING_INVALID = -1, + }; + ++static const char *encoding_names[] = { ++ [ENCODING_ANY] = "ANY", ++ [ENCODING_PCM] = "PCM", ++ [ENCODING_AC3_IEC61937] = "AC3-IEC61937", ++ [ENCODING_EAC3_IEC61937] = "EAC3-IEC61937", ++ [ENCODING_MPEG_IEC61937] = "MPEG-IEC61937", ++ [ENCODING_DTS_IEC61937] = "DTS-IEC61937", ++ [ENCODING_MPEG2_AAC_IEC61937] = "MPEG2-AAC-IEC61937", ++ [ENCODING_TRUEHD_IEC61937] = "TRUEHD-IEC61937", ++ [ENCODING_DTSHD_IEC61937] = "DTSHD-IEC61937", ++}; ++ ++static inline const char *format_encoding2name(enum encoding enc) ++{ ++ if (enc >= 0 && enc < (int)SPA_N_ELEMENTS(encoding_names)) ++ return encoding_names[enc]; ++ return "INVALID"; ++} ++ + struct format_info { + enum encoding encoding; + struct pw_properties *props; +diff --git a/src/modules/module-protocol-pulse/message.c b/src/modules/module-protocol-pulse/message.c +index a0ddce058..e6c7e0a56 100644 +--- a/src/modules/module-protocol-pulse/message.c ++++ b/src/modules/module-protocol-pulse/message.c +@@ -850,7 +850,9 @@ static int message_dump(enum spa_log_level level, struct message *m) + const struct spa_dict_item *it; + if ((res = read_format_info(m, &info)) < 0) + return res; +- pw_log(level, "%u: format-info: n_items:%u", o, info.props->dict.n_items); ++ pw_log(level, "%u: format-info: enc:%s n_items:%u", ++ o, format_encoding2name(info.encoding), ++ info.props->dict.n_items); + spa_dict_for_each(it, &info.props->dict) + pw_log(level, " '%s': '%s'", it->key, it->value); + break; +-- +2.26.2 + diff --git a/0019-pulse-server-handle-unsupported-formats.patch b/0019-pulse-server-handle-unsupported-formats.patch new file mode 100644 index 0000000..b2a1095 --- /dev/null +++ b/0019-pulse-server-handle-unsupported-formats.patch @@ -0,0 +1,176 @@ +From fcf00b3d352d931446a384a768b999520662b599 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Fri, 19 Feb 2021 15:25:32 +0100 +Subject: [PATCH 19/30] pulse-server: handle unsupported formats + +We can only handle PCM encodings for now, fail conversion otherwise. +If we have no supported formats, return an error code. + +VLC first try to send AC3 or EAC3 passthrough and then tries again +with decoded data. If we accept the encoded data we are just playing +noise. + +Fixes #428 +--- + src/modules/module-protocol-pulse/format.c | 3 + + .../module-protocol-pulse/pulse-server.c | 56 ++++++++++++++++--- + 2 files changed, 51 insertions(+), 8 deletions(-) + +diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c +index f718e70a7..4bd0daabe 100644 +--- a/src/modules/module-protocol-pulse/format.c ++++ b/src/modules/module-protocol-pulse/format.c +@@ -563,6 +563,9 @@ static const struct spa_pod *format_info_build_param(struct spa_pod_builder *b, + spa_zero(ss); + spa_zero(map); + ++ if (info->encoding != ENCODING_PCM) ++ return NULL; ++ + if ((str = pw_properties_get(info->props, "format.sample_format")) == NULL) + return NULL; + +diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c +index 1cce96203..977c9886a 100644 +--- a/src/modules/module-protocol-pulse/pulse-server.c ++++ b/src/modules/module-protocol-pulse/pulse-server.c +@@ -1752,6 +1752,16 @@ static const struct pw_stream_events stream_events = + .drained = stream_drained, + }; + ++static void log_format_info(struct impl *impl, enum spa_log_level level, struct format_info *format) ++{ ++ const struct spa_dict_item *it; ++ pw_log(level, NAME" %p: format %s", ++ impl, format_encoding2name(format->encoding)); ++ spa_dict_for_each(it, &format->props->dict) ++ pw_log(level, NAME" %p: '%s': '%s'", ++ impl, it->key, it->value); ++} ++ + static int do_create_playback_stream(struct client *client, uint32_t command, uint32_t tag, struct message *m) + { + struct impl *impl = client->impl; +@@ -1783,7 +1793,7 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui + struct pw_properties *props = NULL; + uint8_t n_formats = 0; + struct stream *stream = NULL; +- uint32_t n_params = 0, flags; ++ uint32_t n_params = 0, n_valid_formats = 0, flags; + const struct spa_pod *params[32]; + uint8_t buffer[4096]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); +@@ -1871,8 +1881,14 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui + + if (sample_spec_valid(&ss)) { + if ((params[n_params] = format_build_param(&b, +- SPA_PARAM_EnumFormat, &ss, &map)) != NULL) ++ SPA_PARAM_EnumFormat, &ss, &map)) != NULL) { + n_params++; ++ n_valid_formats++; ++ } else { ++ pw_log_warn(NAME" %p: unsupported format:%s rate:%d channels:%u", ++ impl, format_id2name(ss.format), ss.rate, ++ ss.channels); ++ } + } + if (client->version >= 21) { + if ((res = message_get(m, +@@ -1891,9 +1907,12 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui + goto error_protocol; + + if ((params[n_params] = format_info_build_param(&b, +- SPA_PARAM_EnumFormat, &format)) != NULL) ++ SPA_PARAM_EnumFormat, &format)) != NULL) { + n_params++; +- ++ n_valid_formats++; ++ } else { ++ log_format_info(impl, SPA_LOG_LEVEL_WARN, &format); ++ } + format_info_clear(&format); + } + } +@@ -1901,6 +1920,9 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui + if (m->offset != m->length) + goto error_protocol; + ++ if (n_valid_formats == 0) ++ goto error_no_formats; ++ + stream = calloc(1, sizeof(struct stream)); + if (stream == NULL) + goto error_errno; +@@ -1968,6 +1990,9 @@ error_errno: + error_protocol: + res = -EPROTO; + goto error; ++error_no_formats: ++ res = -ENOTSUP; ++ goto error; + error_invalid: + res = -EINVAL; + goto error; +@@ -2012,7 +2037,7 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint + struct pw_properties *props = NULL; + uint8_t n_formats = 0; + struct stream *stream = NULL; +- uint32_t n_params = 0, flags, id; ++ uint32_t n_params = 0, n_valid_formats = 0, flags, id; + const struct spa_pod *params[32]; + uint8_t buffer[4096]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); +@@ -2082,8 +2107,14 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint + } + if (sample_spec_valid(&ss)) { + if ((params[n_params] = format_build_param(&b, +- SPA_PARAM_EnumFormat, &ss, &map)) != NULL) ++ SPA_PARAM_EnumFormat, &ss, &map)) != NULL) { + n_params++; ++ n_valid_formats++; ++ } else { ++ pw_log_warn(NAME" %p: unsupported format:%s rate:%d channels:%u", ++ impl, format_id2name(ss.format), ss.rate, ++ ss.channels); ++ } + } + if (client->version >= 22) { + if ((res = message_get(m, +@@ -2102,9 +2133,12 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint + goto error_protocol; + + if ((params[n_params] = format_info_build_param(&b, +- SPA_PARAM_EnumFormat, &format)) != NULL) ++ SPA_PARAM_EnumFormat, &format)) != NULL) { + n_params++; +- ++ n_valid_formats++; ++ } else { ++ log_format_info(impl, SPA_LOG_LEVEL_WARN, &format); ++ } + format_info_clear(&format); + } + } +@@ -2121,6 +2155,9 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint + if (m->offset != m->length) + goto error_protocol; + ++ if (n_valid_formats == 0) ++ goto error_no_formats; ++ + stream = calloc(1, sizeof(struct stream)); + if (stream == NULL) + goto error_errno; +@@ -2201,6 +2238,9 @@ error_errno: + error_protocol: + res = -EPROTO; + goto error; ++error_no_formats: ++ res = -ENOTSUP; ++ goto error; + error_invalid: + res = -EINVAL; + goto error; +-- +2.26.2 + diff --git a/0022-pw-cli-always-output-to-stdout.patch b/0022-pw-cli-always-output-to-stdout.patch new file mode 100644 index 0000000..4f763f9 --- /dev/null +++ b/0022-pw-cli-always-output-to-stdout.patch @@ -0,0 +1,59 @@ +From 91875c1fd8ece6b1b94130c50304b715654f8681 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Fri, 19 Feb 2021 16:42:21 +0100 +Subject: [PATCH 22/30] pw-cli: always output to stdout + +Use spa_debug_pod because spa_debug_format outputs to stderr +--- + src/tools/pw-cli.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +diff --git a/src/tools/pw-cli.c b/src/tools/pw-cli.c +index b682bf34e..7706a15fa 100644 +--- a/src/tools/pw-cli.c ++++ b/src/tools/pw-cli.c +@@ -33,9 +33,10 @@ + #endif + #include + ++#define spa_debug(...) fprintf(stdout,__VA_ARGS__);fputc('\n', stdout) ++ + #include + #include +-#include + #include + #include + #include +@@ -681,7 +682,7 @@ static void info_link(struct proxy_data *pd) + printf("\n"); + fprintf(stdout, "%c\tformat:\n", MARK_CHANGE(PW_LINK_CHANGE_MASK_FORMAT)); + if (info->format) +- spa_debug_format(2, NULL, info->format); ++ spa_debug_pod(2, NULL, info->format); + else + fprintf(stdout, "\t\tnone\n"); + print_properties(info->props, MARK_CHANGE(PW_LINK_CHANGE_MASK_PROPS), true); +@@ -815,10 +816,7 @@ static void event_param(void *object, int seq, uint32_t id, + fprintf(stdout, "remote %d object %d param %d index %d\n", + rd->id, data->global->id, id, index); + +- if (spa_pod_is_object_type(param, SPA_TYPE_OBJECT_Format)) +- spa_debug_format(2, NULL, param); +- else +- spa_debug_pod(2, NULL, param); ++ spa_debug_pod(2, NULL, param); + } + + static const struct pw_node_events node_events = { +@@ -2567,7 +2565,7 @@ dump_link(struct data *data, struct global *global, + printf("\n"); + fprintf(stdout, "%sformat:\n", ind); + if (info->format) +- spa_debug_format(8 * (level + 1) + 2, NULL, info->format); ++ spa_debug_pod(8 * (level + 1) + 2, NULL, info->format); + else + fprintf(stdout, "%s\tnone\n", ind); + +-- +2.26.2 + diff --git a/0024-policy-node-don-t-crash-without-metadata.patch b/0024-policy-node-don-t-crash-without-metadata.patch new file mode 100644 index 0000000..f0b9767 --- /dev/null +++ b/0024-policy-node-don-t-crash-without-metadata.patch @@ -0,0 +1,26 @@ +From 3673265ae20d7b59e89cad6c5238c232796731b2 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Fri, 19 Feb 2021 17:54:31 +0100 +Subject: [PATCH 24/30] policy-node: don't crash without metadata + +--- + src/examples/media-session/policy-node.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/examples/media-session/policy-node.c b/src/examples/media-session/policy-node.c +index 89ec31729..3bcab7df4 100644 +--- a/src/examples/media-session/policy-node.c ++++ b/src/examples/media-session/policy-node.c +@@ -810,6 +810,9 @@ static void refresh_auto_default_nodes(struct impl *impl) + { + struct default_node *def; + ++ if (impl->session->metadata == NULL) ++ return; ++ + /* Auto set default nodes */ + for (def = impl->defaults; def->key != NULL; ++def) { + struct node *node; +-- +2.26.2 + diff --git a/0025-bluez5-route-shouldn-t-list-a2dp-profiles-when-not-c.patch b/0025-bluez5-route-shouldn-t-list-a2dp-profiles-when-not-c.patch new file mode 100644 index 0000000..ac987d5 --- /dev/null +++ b/0025-bluez5-route-shouldn-t-list-a2dp-profiles-when-not-c.patch @@ -0,0 +1,26 @@ +From a5dc2493dfccfc168cef01162f38f90e986b8af4 Mon Sep 17 00:00:00 2001 +From: Pauli Virtanen +Date: Fri, 19 Feb 2021 21:16:27 +0200 +Subject: [PATCH 25/30] bluez5: route shouldn't list a2dp profiles when not + connected + +--- + spa/plugins/bluez5/bluez5-device.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c +index ea9930f76..f6a1572f8 100644 +--- a/spa/plugins/bluez5/bluez5-device.c ++++ b/spa/plugins/bluez5/bluez5-device.c +@@ -448,6 +448,8 @@ static uint32_t profile_direction_mask(struct impl *this, uint32_t index) + have_output = true; + else if (profile == SPA_BT_PROFILE_A2DP_SOURCE) + have_input = true; ++ else if (profile == 0) ++ have_output = have_input = false; + else + have_output = have_input = true; + break; +-- +2.26.2 + diff --git a/pipewire.spec b/pipewire.spec index 757b71d..da5615a 100644 --- a/pipewire.spec +++ b/pipewire.spec @@ -8,7 +8,7 @@ %global libversion %{soversion}.%(bash -c '((intversion = (%{minorversion} * 100) + %{microversion})); echo ${intversion}').0 # For rpmdev-bumpspec and releng automation -%global baserelease 4 +%global baserelease 5 #global snapdate 20210107 #global gitcommit b17db2cebc1a5ab2c01851d29c05f79cd2f262bb @@ -52,6 +52,18 @@ Source0: https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/%{version}/p %endif ## upstream patches +Patch0: 0001-bluez5-include-a2dp-codec-profiles-in-route-profiles.patch +Patch1: 0005-fix-some-warnings.patch +Patch2: 0006-spa-escape-double-quotes.patch +Patch3: 0009-bluez5-volumes-need-to-be-distributed-to-all-channel.patch +Patch4: 0011-bluez5-backend-native-Check-volume-values.patch +Patch5: 0012-media-session-don-t-switch-to-pro-audio-by-default.patch +Patch6: 0013-audioconvert-keep-better-track-of-param-changes.patch +Patch7: 0018-pulse-server-print-encoding-name-in-format_info.patch +Patch8: 0019-pulse-server-handle-unsupported-formats.patch +Patch9: 0022-pw-cli-always-output-to-stdout.patch +Patch10: 0024-policy-node-don-t-crash-without-metadata.patch +Patch11: 0025-bluez5-route-shouldn-t-list-a2dp-profiles-when-not-c.patch ## upstreamable patches @@ -435,6 +447,9 @@ systemctl --no-reload preset --global pipewire.socket >/dev/null 2>&1 || : %endif %changelog +* Mon Feb 22 2021 Wim Taymans - 0.3.22-5 +- Add some critical patches + * Fri Feb 19 2021 Neal Gompa - 0.3.22-4 - Replace more PulseAudio modules on upgrade in F34+