diff --git a/spa/plugins/bluez5/a2dp-codec-aac.c b/spa/plugins/bluez5/a2dp-codec-aac.c index 6d4db7fc4..c49f7a616 100644 --- a/spa/plugins/bluez5/a2dp-codec-aac.c +++ b/spa/plugins/bluez5/a2dp-codec-aac.c @@ -139,7 +139,8 @@ static int get_valid_aac_bitrate(a2dp_aac_t *conf) static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_aac_t conf; int i; diff --git a/spa/plugins/bluez5/a2dp-codec-aptx.c b/spa/plugins/bluez5/a2dp-codec-aptx.c index 75ceb229f..89c4d69bd 100644 --- a/spa/plugins/bluez5/a2dp-codec-aptx.c +++ b/spa/plugins/bluez5/a2dp-codec-aptx.c @@ -110,7 +110,8 @@ aptx_frequencies[] = { static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_aptx_t conf; int i; @@ -146,7 +147,8 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags, static int codec_select_config_ll(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_aptx_ll_ext_t conf = { 0 }; size_t actual_conf_size; @@ -166,7 +168,7 @@ static int codec_select_config_ll(const struct media_codec *codec, uint32_t flag if (codec->duplex_codec && !conf.base.bidirect_link) return -ENOTSUP; - if ((res = codec_select_config(codec, flags, caps, caps_size, info, settings, config)) < 0) + if ((res = codec_select_config(codec, flags, caps, caps_size, info, settings, config, NULL)) < 0) return res; memcpy(&conf.base.aptx, config, sizeof(conf.base.aptx)); diff --git a/spa/plugins/bluez5/a2dp-codec-faststream.c b/spa/plugins/bluez5/a2dp-codec-faststream.c index edc833292..87a438a3f 100644 --- a/spa/plugins/bluez5/a2dp-codec-faststream.c +++ b/spa/plugins/bluez5/a2dp-codec-faststream.c @@ -61,7 +61,8 @@ duplex_frequencies[] = { static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_faststream_t conf; int i; diff --git a/spa/plugins/bluez5/a2dp-codec-lc3plus.c b/spa/plugins/bluez5/a2dp-codec-lc3plus.c index ee25fef0c..b6bf1c653 100644 --- a/spa/plugins/bluez5/a2dp-codec-lc3plus.c +++ b/spa/plugins/bluez5/a2dp-codec-lc3plus.c @@ -83,7 +83,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_lc3plus_hr_t conf; @@ -137,8 +138,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f int a, b; /* 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); + res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1, NULL); + res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2, NULL); #define PREFER_EXPR(expr) \ do { \ diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index 11185800e..c80733220 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -115,7 +115,8 @@ ldac_channel_modes[] = { static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_ldac_t conf; int i; diff --git a/spa/plugins/bluez5/a2dp-codec-opus-g.c b/spa/plugins/bluez5/a2dp-codec-opus-g.c index 130dea433..0f9b2a200 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus-g.c +++ b/spa/plugins/bluez5/a2dp-codec-opus-g.c @@ -74,7 +74,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_opus_g_t conf; int frequency, duration, channels; @@ -126,8 +127,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f int a, b; /* Order selected configurations by preference */ - res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1); - res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2); + res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1, NULL); + res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, NULL); #define PREFER_EXPR(expr) \ do { \ diff --git a/spa/plugins/bluez5/a2dp-codec-opus.c b/spa/plugins/bluez5/a2dp-codec-opus.c index c5b042b20..c1bf5e030 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus.c +++ b/spa/plugins/bluez5/a2dp-codec-opus.c @@ -590,7 +590,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { struct props props; a2dp_opus_05_t conf; @@ -699,8 +700,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f int a, b; /* Order selected configurations by preference */ - res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1); - res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2); + res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1, NULL); + res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, NULL); #define PREFER_EXPR(expr) \ do { \ diff --git a/spa/plugins/bluez5/a2dp-codec-sbc.c b/spa/plugins/bluez5/a2dp-codec-sbc.c index a91109e52..b4ebfc2a2 100644 --- a/spa/plugins/bluez5/a2dp-codec-sbc.c +++ b/spa/plugins/bluez5/a2dp-codec-sbc.c @@ -135,7 +135,8 @@ sbc_xq_channel_modes[] = { static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { a2dp_sbc_t conf; int bitpool, i; @@ -221,8 +222,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f bool xq = (spa_streq(codec->name, "sbc_xq")); /* 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); + res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1, NULL); + res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2, NULL); #define PREFER_EXPR(expr) \ do { \ diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c index cdc6f8546..0319e7502 100644 --- a/spa/plugins/bluez5/bap-codec-lc3.c +++ b/spa/plugins/bluez5/bap-codec-lc3.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -47,13 +48,17 @@ struct settings { uint32_t channel_allocation; bool sink; bool duplex; - const char *qos_name; + char *qos_name; int retransmission; int latency; int64_t delay; int framing; }; +struct config_data { + struct settings settings; +}; + struct pac_data { const uint8_t *data; size_t size; @@ -771,7 +776,7 @@ static void parse_settings(struct settings *s, const struct spa_dict *settings, return; if ((str = spa_dict_lookup(settings, "bluez5.bap.preset"))) - s->qos_name = str; + s->qos_name = strdup(str); if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.rtn"), &value, 0)) s->retransmission = value; @@ -811,23 +816,38 @@ static void parse_settings(struct settings *s, const struct spa_dict *settings, (int)s->sink, (int)s->duplex); } +static void free_config_data(struct config_data *d) +{ + if (!d) + return; + free(d->settings.qos_name); + free(d); +} + +SPA_DEFINE_AUTOPTR_CLEANUP(config_data, struct config_data, { spa_clear_ptr(*thing, free_config_data); }); + static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data) { struct pac_data pacs[MAX_PACS]; int npacs; bap_lc3_t conf; struct spa_debug_log_ctx debug_ctx; - struct settings s; - int i; + spa_autoptr(config_data) d = NULL; + int i, ret; struct ltv_writer writer = LTV_WRITER(config, A2DP_MAX_CAPS_SIZE); if (caps == NULL) return -EINVAL; - parse_settings(&s, settings, &debug_ctx); + d = calloc(1, sizeof(*d)); + if (!d) + return -ENOMEM; + + parse_settings(&d->settings, settings, &debug_ctx); /* Select best conf from those possible */ npacs = parse_bluez_pacs(caps, caps_size, pacs, &debug_ctx.ctx); @@ -840,7 +860,7 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags, } for (i = 0; i < npacs; ++i) - pacs[i].settings = &s; + pacs[i].settings = &d->settings; qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp); @@ -859,7 +879,12 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags, ltv_writer_uint16(&writer, LC3_TYPE_FRAMELEN, conf.framelen); ltv_writer_uint8(&writer, LC3_TYPE_BLKS, conf.n_blks); - return ltv_writer_end(&writer); + ret = ltv_writer_end(&writer); + + if (ret >= 0 && config_data) + *config_data = spa_steal_ptr(d); + + return ret; } static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, @@ -869,8 +894,8 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f int res1, res2; /* Order selected configurations by preference */ - res1 = codec->select_config(codec, 0, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1); - res2 = codec->select_config(codec, 0, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2); + res1 = codec->select_config(codec, 0, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1, NULL); + res2 = codec->select_config(codec, 0, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2, NULL); return conf_cmp(&conf1, res1, &conf2, res2); } @@ -1038,22 +1063,22 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags static int codec_get_qos(const struct media_codec *codec, const void *config, size_t config_size, const struct bap_endpoint_qos *endpoint_qos, - struct bap_codec_qos *qos, const struct spa_dict *settings) + const void *config_data, + struct bap_codec_qos *qos) { struct bap_qos bap_qos; bap_lc3_t conf; bool found = false; - struct settings s; - struct spa_debug_log_ctx debug_ctx; + const struct config_data *d = config_data; spa_zero(*qos); + if (!d) + return -EINVAL; if (!parse_conf(&conf, config, config_size)) return -EINVAL; - parse_settings(&s, settings, &debug_ctx); - - found = select_bap_qos(&bap_qos, &s, get_rate_mask(conf.rate), get_duration_mask(conf.frame_duration), + found = select_bap_qos(&bap_qos, &d->settings, get_rate_mask(conf.rate), get_duration_mask(conf.frame_duration), conf.framelen, conf.framelen); if (!found) { /* shouldn't happen: select_config should pick existing one */ @@ -1096,6 +1121,11 @@ static int codec_get_qos(const struct media_codec *codec, return 0; } +static void codec_free_config_data(const struct media_codec *codec, void *config_data) +{ + free_config_data(config_data); +} + static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) @@ -1419,6 +1449,7 @@ const struct media_codec bap_codec_lc3 = { .enum_config = codec_enum_config, .validate_config = codec_validate_config, .get_qos = codec_get_qos, + .free_config_data = codec_free_config_data, .caps_preference_cmp = codec_caps_preference_cmp, .init = codec_init, .deinit = codec_deinit, diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index f254e8e36..9bfe141d0 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -695,7 +695,7 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu * by codec switching. */ res = codec->select_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, cap, size, &monitor->default_audio_info, - &monitor->global_settings, config); + &monitor->global_settings, config, NULL); else res = -ENOTSUP; @@ -1032,6 +1032,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe const char *endpoint_path = NULL; uint8_t config[A2DP_MAX_CAPS_SIZE]; + void *config_data = NULL; char locations[64] = {0}; char channel_allocation[64] = {0}; int conf_size; @@ -1048,6 +1049,9 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe path = dbus_message_get_path(m); + if ((r = dbus_message_new_method_return(m)) == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + /* TODO: for codecs with shared endpoint, this currently always picks the default * one. However, currently we don't have BAP codecs with shared endpoint, so * this does not matter, but in case they are needed later we should pick the @@ -1105,7 +1109,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe settings = SPA_DICT_INIT(setting_items, i); conf_size = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len, - &monitor->default_audio_info, &settings, config); + &monitor->default_audio_info, &settings, config, &config_data); if (conf_size < 0) { spa_log_error(monitor->log, "can't select config: %d (%s)", conf_size, spa_strerror(conf_size)); @@ -1114,8 +1118,6 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe spa_log_info(monitor->log, "%p: selected conf %d", monitor, conf_size); spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', (uint8_t *)config, (size_t)conf_size); - if ((r = dbus_message_new_method_return(m)) == NULL) - return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_message_iter_init_append(r, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, @@ -1134,7 +1136,7 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe spa_zero(qos); - res = codec->get_qos(codec, config, conf_size, &ep->qos, &qos, &settings); + res = codec->get_qos(codec, config, conf_size, &ep->qos, config_data, &qos); if (res < 0) { spa_log_error(monitor->log, "can't select QOS config: %d (%s)", res, spa_strerror(res)); @@ -1184,6 +1186,9 @@ static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMe dbus_message_iter_close_container(&iter, &dict); + if (config_data && codec->free_config_data) + codec->free_config_data(codec, config_data); + if (!dbus_connection_send(conn, r, NULL)) return DBUS_HANDLER_RESULT_NEED_MEMORY; @@ -1194,6 +1199,9 @@ error_invalid: goto error; error: + if (config_data && codec->free_config_data) + codec->free_config_data(codec, config_data); + if (!reply_with_error(conn, m, "org.bluez.Error.InvalidArguments", err_msg)) return DBUS_HANDLER_RESULT_NEED_MEMORY; return DBUS_HANDLER_RESULT_HANDLED; @@ -4506,7 +4514,7 @@ static bool codec_switch_configure_a2dp(struct spa_bt_codec_switch *sw, const ch } res = codec->select_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, ep->capabilities, ep->capabilities_len, - &monitor->default_audio_info, &monitor->global_settings, config); + &monitor->default_audio_info, &monitor->global_settings, config, NULL); if (res < 0) { spa_log_error(monitor->log, "media codec switch %p: incompatible capabilities (%d)", sw, res); diff --git a/spa/plugins/bluez5/media-codecs.c b/spa/plugins/bluez5/media-codecs.c index 191b114cb..1b37050cd 100644 --- a/spa/plugins/bluez5/media-codecs.c +++ b/spa/plugins/bluez5/media-codecs.c @@ -92,7 +92,7 @@ bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_ if (caps == NULL) return false; - res = codec->select_config(codec, 0, caps, caps_size, info, global_settings, config); + res = codec->select_config(codec, 0, caps, caps_size, info, global_settings, config, NULL); if (res < 0) return false; diff --git a/spa/plugins/bluez5/media-codecs.h b/spa/plugins/bluez5/media-codecs.h index 46cee2acb..b817ef04a 100644 --- a/spa/plugins/bluez5/media-codecs.h +++ b/spa/plugins/bluez5/media-codecs.h @@ -26,7 +26,7 @@ #define SPA_TYPE_INTERFACE_Bluez5CodecMedia SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:Media:Private" -#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 15 +#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 16 struct spa_bluez5_codec_a2dp { struct spa_interface iface; @@ -103,7 +103,8 @@ struct media_codec { int (*select_config) (const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, - const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE]); + const struct spa_dict *global_settings, uint8_t config[A2DP_MAX_CAPS_SIZE], + void **config_data); int (*enum_config) (const struct media_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); @@ -113,7 +114,9 @@ struct media_codec { int (*get_qos)(const struct media_codec *codec, const void *config, size_t config_size, const struct bap_endpoint_qos *endpoint_qos, - struct bap_codec_qos *qos, const struct spa_dict *settings); + const void *config_data, + struct bap_codec_qos *qos); + void (*free_config_data)(const struct media_codec *codec, void *config_data); /** qsort comparison sorting caps in order of preference for the codec. * Used in codec switching to select best remote endpoints.