From a8eb146d392af32ede210341220c372148f5ee37 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 21 May 2022 13:18:38 +0300 Subject: [PATCH] bluez5: tell the codec whether endpoint is sink or source Add a flag A2DP_CODEC_FLAG_SINK to incidate a sink endpoint. Also enum_config and caps_preference_cmp may need to know whether the codec is being configured for SRC or SNK. Also add the flags argument to init_props. Bump codec API version. --- spa/plugins/bluez5/a2dp-codec-aac.c | 4 +- spa/plugins/bluez5/a2dp-codec-aptx.c | 4 +- spa/plugins/bluez5/a2dp-codec-faststream.c | 4 +- spa/plugins/bluez5/a2dp-codec-lc3plus.c | 6 +-- spa/plugins/bluez5/a2dp-codec-ldac.c | 4 +- spa/plugins/bluez5/a2dp-codec-sbc.c | 4 +- spa/plugins/bluez5/a2dp-codecs.h | 9 ++-- spa/plugins/bluez5/a2dp-sink.c | 5 ++- spa/plugins/bluez5/a2dp-source.c | 5 ++- spa/plugins/bluez5/bluez5-dbus.c | 52 +++++++++++++++------- 10 files changed, 63 insertions(+), 34 deletions(-) diff --git a/spa/plugins/bluez5/a2dp-codec-aac.c b/spa/plugins/bluez5/a2dp-codec-aac.c index 4f0d3c3a8..80fbb8afc 100644 --- a/spa/plugins/bluez5/a2dp-codec-aac.c +++ b/spa/plugins/bluez5/a2dp-codec-aac.c @@ -170,7 +170,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, return sizeof(conf); } -static int codec_enum_config(const struct a2dp_codec *codec, +static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -289,7 +289,7 @@ static int codec_validate_config(const struct a2dp_codec *codec, uint32_t flags, return 0; } -static void *codec_init_props(const struct a2dp_codec *codec, const struct spa_dict *settings) +static void *codec_init_props(const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings) { struct props *p = calloc(1, sizeof(struct props)); const char *str; diff --git a/spa/plugins/bluez5/a2dp-codec-aptx.c b/spa/plugins/bluez5/a2dp-codec-aptx.c index 9a80134fd..4af804cd1 100644 --- a/spa/plugins/bluez5/a2dp-codec-aptx.c +++ b/spa/plugins/bluez5/a2dp-codec-aptx.c @@ -218,7 +218,7 @@ static int codec_select_config_ll(const struct a2dp_codec *codec, uint32_t flags return actual_conf_size; } -static int codec_enum_config(const struct a2dp_codec *codec, +static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -458,7 +458,7 @@ static int codec_decode(void *data, * When connected as SRC to SNK, aptX-LL sink may send back mSBC data. */ -static int msbc_enum_config(const struct a2dp_codec *codec, +static int msbc_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { diff --git a/spa/plugins/bluez5/a2dp-codec-faststream.c b/spa/plugins/bluez5/a2dp-codec-faststream.c index ddcf331d6..7ad415645 100644 --- a/spa/plugins/bluez5/a2dp-codec-faststream.c +++ b/spa/plugins/bluez5/a2dp-codec-faststream.c @@ -129,7 +129,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, return sizeof(conf); } -static int codec_enum_config(const struct a2dp_codec *codec, +static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -372,7 +372,7 @@ static SPA_UNUSED int codec_decode(void *data, * When connected as SRC to SNK, FastStream sink may send back SBC data. */ -static int duplex_enum_config(const struct a2dp_codec *codec, +static int duplex_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { diff --git a/spa/plugins/bluez5/a2dp-codec-lc3plus.c b/spa/plugins/bluez5/a2dp-codec-lc3plus.c index b50c94746..6aa06c6a4 100644 --- a/spa/plugins/bluez5/a2dp-codec-lc3plus.c +++ b/spa/plugins/bluez5/a2dp-codec-lc3plus.c @@ -150,7 +150,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, return sizeof(conf); } -static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void *caps1, size_t caps1_size, +static int codec_caps_preference_cmp(const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info) { a2dp_lc3plus_hr_t conf1, conf2; @@ -160,7 +160,7 @@ static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void /* Order selected configurations by preference */ res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1); - res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2); + res2 = codec->select_config(codec, 0, caps2, caps2_size, info, NULL, (uint8_t *)&conf2); #define PREFER_EXPR(expr) \ do { \ @@ -190,7 +190,7 @@ static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void #undef PREFER_BOOL } -static int codec_enum_config(const struct a2dp_codec *codec, +static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index ee6bc2ebf..450f9765e 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -150,7 +150,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, return sizeof(conf); } -static int codec_enum_config(const struct a2dp_codec *codec, +static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -284,7 +284,7 @@ static int string_to_eqmid(const char * eqmid) return LDACBT_EQMID_AUTO; } -static void *codec_init_props(const struct a2dp_codec *codec, const struct spa_dict *settings) +static void *codec_init_props(const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings) { struct props *p = calloc(1, sizeof(struct props)); const char *str; diff --git a/spa/plugins/bluez5/a2dp-codec-sbc.c b/spa/plugins/bluez5/a2dp-codec-sbc.c index 6a43c666a..1bc85914d 100644 --- a/spa/plugins/bluez5/a2dp-codec-sbc.c +++ b/spa/plugins/bluez5/a2dp-codec-sbc.c @@ -229,7 +229,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, return sizeof(conf); } -static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void *caps1, size_t caps1_size, +static int codec_caps_preference_cmp(const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info) { a2dp_sbc_t conf1, conf2; @@ -356,7 +356,7 @@ static int codec_set_bitpool(struct impl *this, int bitpool) return this->sbc.bitpool; } -static int codec_enum_config(const struct a2dp_codec *codec, +static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { diff --git a/spa/plugins/bluez5/a2dp-codecs.h b/spa/plugins/bluez5/a2dp-codecs.h index bd041b29e..4e393df12 100644 --- a/spa/plugins/bluez5/a2dp-codecs.h +++ b/spa/plugins/bluez5/a2dp-codecs.h @@ -43,7 +43,7 @@ #define SPA_TYPE_INTERFACE_Bluez5CodecA2DP SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:A2DP:Private" -#define SPA_VERSION_BLUEZ5_CODEC_A2DP 1 +#define SPA_VERSION_BLUEZ5_CODEC_A2DP 2 struct spa_bluez5_codec_a2dp { struct spa_interface iface; @@ -62,6 +62,7 @@ extern const struct a2dp_codec * const * const codec_plugin_a2dp_codecs; extern const char *codec_plugin_factory_name; #endif +#define A2DP_CODEC_FLAG_SINK (1 << 0) #define A2DP_CODEC_DEFAULT_RATE 48000 #define A2DP_CODEC_DEFAULT_CHANNELS 2 @@ -97,7 +98,7 @@ struct a2dp_codec { const void *caps, size_t caps_size, const struct a2dp_codec_audio_info *info, const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]); - int (*enum_config) (const struct a2dp_codec *codec, + int (*enum_config) (const struct a2dp_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *builder, struct spa_pod **param); int (*validate_config) (const struct a2dp_codec *codec, uint32_t flags, @@ -109,10 +110,10 @@ struct a2dp_codec { * The caps handed in correspond to this codec_id, but are * otherwise not checked beforehand. */ - int (*caps_preference_cmp) (const struct a2dp_codec *codec, const void *caps1, size_t caps1_size, + int (*caps_preference_cmp) (const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info); - void *(*init_props) (const struct a2dp_codec *codec, const struct spa_dict *settings); + void *(*init_props) (const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings); void (*clear_props) (void *); int (*enum_props) (void *props, const struct spa_dict *settings, uint32_t id, uint32_t idx, struct spa_pod_builder *builder, struct spa_pod **param); diff --git a/spa/plugins/bluez5/a2dp-sink.c b/spa/plugins/bluez5/a2dp-sink.c index d0d78d8a7..2e7b057de 100644 --- a/spa/plugins/bluez5/a2dp-sink.c +++ b/spa/plugins/bluez5/a2dp-sink.c @@ -910,7 +910,8 @@ static int do_start(struct impl *this) for (i = 0; i < size; i++) spa_log_debug(this->log, " %d: %02x", i, conf[i]); - this->codec_data = this->codec->init(this->codec, 0, + this->codec_data = this->codec->init(this->codec, + this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0, this->transport->configuration, this->transport->configuration_len, &port->current_format, @@ -1197,6 +1198,7 @@ impl_node_port_enum_params(void *object, int seq, return -EIO; if ((res = this->codec->enum_config(this->codec, + this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0, this->transport->configuration, this->transport->configuration_len, id, result.index, &b, ¶m)) != 1) @@ -1726,6 +1728,7 @@ impl_init(const struct spa_handle_factory *factory, if (this->codec->init_props != NULL) this->codec_props = this->codec->init_props(this->codec, + this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0, this->transport->device->settings); reset_props(this, &this->props); diff --git a/spa/plugins/bluez5/a2dp-source.c b/spa/plugins/bluez5/a2dp-source.c index e94c3bd3a..b7787323a 100644 --- a/spa/plugins/bluez5/a2dp-source.c +++ b/spa/plugins/bluez5/a2dp-source.c @@ -630,7 +630,8 @@ static int transport_start(struct impl *this) this->transport_acquired = true; - this->codec_data = this->codec->init(this->codec, 0, + this->codec_data = this->codec->init(this->codec, + this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK, this->transport->configuration, this->transport->configuration_len, &port->current_format, @@ -967,6 +968,7 @@ impl_node_port_enum_params(void *object, int seq, return -EIO; if ((res = this->codec->enum_config(this->codec, + this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK, this->transport->configuration, this->transport->configuration_len, id, result.index, &b, ¶m)) != 1) @@ -1581,6 +1583,7 @@ impl_init(const struct spa_handle_factory *factory, if (this->codec->init_props != NULL) this->codec_props = this->codec->init_props(this->codec, + this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK, this->transport->device->settings); spa_bt_transport_add_listener(this->transport, diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index be99188ba..3234de3a3 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -437,18 +437,21 @@ static int a2dp_codec_to_endpoint(const struct a2dp_codec *codec, return 0; } -static const struct a2dp_codec *a2dp_endpoint_to_codec(struct spa_bt_monitor *monitor, const char *endpoint) +static const struct a2dp_codec *a2dp_endpoint_to_codec(struct spa_bt_monitor *monitor, const char *endpoint, bool *sink) { const char *ep_name; const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs; int i; - if (spa_strstartswith(endpoint, A2DP_SINK_ENDPOINT "/")) + if (spa_strstartswith(endpoint, A2DP_SINK_ENDPOINT "/")) { ep_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/"); - else if (spa_strstartswith(endpoint, A2DP_SOURCE_ENDPOINT "/")) + *sink = true; + } else if (spa_strstartswith(endpoint, A2DP_SOURCE_ENDPOINT "/")) { ep_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/"); - else + *sink = false; + } else { return NULL; + } for (i = 0; a2dp_codecs[i]; i++) { const struct a2dp_codec *codec = a2dp_codecs[i]; @@ -486,6 +489,7 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu DBusError err; int i, size, res; const struct a2dp_codec *codec; + bool sink; dbus_error_init(&err); @@ -501,14 +505,14 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu for (i = 0; i < size; i++) spa_log_debug(monitor->log, " %d: %02x", i, cap[i]); - codec = a2dp_endpoint_to_codec(monitor, path); + codec = a2dp_endpoint_to_codec(monitor, path, &sink); if (codec != NULL) /* FIXME: We can't determine which device the SelectConfiguration() * call is associated with, therefore device settings are not passed. * This causes inconsistency with SelectConfiguration() triggered * by codec switching. */ - res = codec->select_config(codec, 0, cap, size, &monitor->default_audio_info, NULL, config); + res = codec->select_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, cap, size, &monitor->default_audio_info, NULL, config); else res = -ENOTSUP; @@ -2573,6 +2577,7 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s DBusMessage *m; DBusMessageIter iter, d; int i; + bool sink; /* Try setting configuration for current codec on current endpoint in list */ @@ -2603,8 +2608,10 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s if (sw->profile & SPA_BT_PROFILE_A2DP_SINK) { local_endpoint_base = A2DP_SOURCE_ENDPOINT; + sink = false; } else if (sw->profile & SPA_BT_PROFILE_A2DP_SOURCE) { local_endpoint_base = A2DP_SINK_ENDPOINT; + sink = true; } else { spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: bad profile (%d), try next", sw, sw->profile); @@ -2630,7 +2637,7 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s } } - res = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len, + res = codec->select_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, ep->capabilities, ep->capabilities_len, &sw->device->monitor->default_audio_info, sw->device->settings, config); if (res < 0) { @@ -2878,6 +2885,7 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b) const struct a2dp_codec *codec = *sw->codec_iter; const char *path1 = *(char **)a, *path2 = *(char **)b; struct spa_bt_remote_endpoint *ep1, *ep2; + uint32_t flags; ep1 = device_remote_endpoint_find(sw->device, path1); ep2 = device_remote_endpoint_find(sw->device, path2); @@ -2886,6 +2894,10 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b) ep1 = NULL; if (ep2 != NULL && (ep2->uuid == NULL || ep2->codec != codec->codec_id || ep2->capabilities == NULL)) ep2 = NULL; + if (ep1 && ep2 && !spa_streq(ep1->uuid, ep2->uuid)) { + ep1 = NULL; + ep2 = NULL; + } if (ep1 == NULL && ep2 == NULL) return 0; @@ -2894,7 +2906,9 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b) else if (ep2 == NULL) return -1; - return codec->caps_preference_cmp(codec, ep1->capabilities, ep1->capabilities_len, + flags = spa_streq(ep1->uuid, SPA_BT_UUID_A2DP_SOURCE) ? A2DP_CODEC_FLAG_SINK : 0; + + return codec->caps_preference_cmp(codec, flags, ep1->capabilities, ep1->capabilities_len, ep2->capabilities, ep2->capabilities_len, &sw->device->monitor->default_audio_info); } @@ -3031,6 +3045,7 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn, struct spa_bt_transport *transport; const struct a2dp_codec *codec; int profile; + bool sink; if (!dbus_message_has_signature(m, "oa{sv}")) { spa_log_warn(monitor->log, "invalid SetConfiguration() signature"); @@ -3039,7 +3054,7 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn, endpoint = dbus_message_get_path(m); profile = a2dp_endpoint_to_profile(endpoint); - codec = a2dp_endpoint_to_codec(monitor, endpoint); + codec = a2dp_endpoint_to_codec(monitor, endpoint, &sink); if (codec == NULL) { spa_log_warn(monitor->log, "unknown SetConfiguration() codec"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -3100,7 +3115,7 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn, if (codec->validate_config) { struct spa_audio_info info; - if (codec->validate_config(codec, 0, + if (codec->validate_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, transport->configuration, transport->configuration_len, &info) < 0) { spa_log_error(monitor->log, "invalid transport configuration"); @@ -3290,12 +3305,15 @@ static int bluez_register_endpoint(struct spa_bt_monitor *monitor, uint8_t caps[A2DP_MAX_CAPS_SIZE]; int ret, caps_size; uint16_t codec_id = codec->codec_id; + bool sink; ret = a2dp_codec_to_endpoint(codec, endpoint, &object_path); if (ret < 0) goto error; - ret = caps_size = codec->fill_caps(codec, 0, caps); + sink = spa_streq(endpoint, A2DP_SINK_ENDPOINT); + + ret = caps_size = codec->fill_caps(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, caps); if (ret < 0) goto error; @@ -3501,11 +3519,11 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage * if (!is_a2dp_codec_enabled(monitor, codec)) continue; - caps_size = codec->fill_caps(codec, 0, caps); - if (caps_size < 0) - continue; - if (codec->decode != NULL) { + caps_size = codec->fill_caps(codec, A2DP_CODEC_FLAG_SINK, caps); + if (caps_size < 0) + continue; + ret = a2dp_codec_to_endpoint(codec, A2DP_SINK_ENDPOINT, &endpoint); if (ret == 0) { spa_log_info(monitor->log, "register A2DP sink codec %s: %s", a2dp_codecs[i]->name, endpoint); @@ -3516,6 +3534,10 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage * } if (codec->encode != NULL) { + caps_size = codec->fill_caps(codec, 0, caps); + if (caps_size < 0) + continue; + ret = a2dp_codec_to_endpoint(codec, A2DP_SOURCE_ENDPOINT, &endpoint); if (ret == 0) { spa_log_info(monitor->log, "register A2DP source codec %s: %s", a2dp_codecs[i]->name, endpoint);