From 1d390addb16f323845669cecb9376ee752da0e7f Mon Sep 17 00:00:00 2001 From: Huang-Huang Bao Date: Sat, 20 Mar 2021 14:53:18 +0800 Subject: [PATCH] a2dp: allow codec to hold Props params Initial Props value are parsed from device settings, further changes are triggered by 'set_param' on a2dp node. Codec can then use props to tweak its transcoder. --- spa/plugins/bluez5/a2dp-codec-aac.c | 2 +- spa/plugins/bluez5/a2dp-codec-aptx.c | 2 +- spa/plugins/bluez5/a2dp-codec-ldac.c | 44 +++++++++++++++++++++++----- spa/plugins/bluez5/a2dp-codec-sbc.c | 2 +- spa/plugins/bluez5/a2dp-codecs.h | 10 +++++-- spa/plugins/bluez5/a2dp-sink.c | 42 ++++++++++++++++++++++---- spa/plugins/bluez5/a2dp-source.c | 44 ++++++++++++++++++++++++---- 7 files changed, 123 insertions(+), 23 deletions(-) diff --git a/spa/plugins/bluez5/a2dp-codec-aac.c b/spa/plugins/bluez5/a2dp-codec-aac.c index b5db6c6ef..1844da64c 100644 --- a/spa/plugins/bluez5/a2dp-codec-aac.c +++ b/spa/plugins/bluez5/a2dp-codec-aac.c @@ -235,7 +235,7 @@ static int codec_enum_config(const struct a2dp_codec *codec, static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, - const struct spa_dict *settings, size_t mtu) + void *props, size_t mtu) { struct impl *this; a2dp_aac_t *conf = config; diff --git a/spa/plugins/bluez5/a2dp-codec-aptx.c b/spa/plugins/bluez5/a2dp-codec-aptx.c index d1bf24fb0..55542b700 100644 --- a/spa/plugins/bluez5/a2dp-codec-aptx.c +++ b/spa/plugins/bluez5/a2dp-codec-aptx.c @@ -223,7 +223,7 @@ static int codec_get_block_size(void *data) static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, - const struct spa_dict *settings, size_t mtu) + void *props, size_t mtu) { struct impl *this; int res; diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index 0d0005b03..afb14eed3 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -39,6 +39,8 @@ #include "rtp.h" #include "a2dp-codecs.h" +#define LDACBT_EQMID_AUTO -1 + #define LDAC_ABR_MAX_PACKET_NBYTES 1280 #define LDAC_ABR_INTERVAL_MS 5 /* 2 frames * 128 lsu / 48000 */ @@ -51,6 +53,10 @@ #define LDAC_ABR_SOCK_BUFFER_SIZE (LDAC_ABR_THRESHOLD_CRITICAL * LDAC_ABR_MAX_PACKET_NBYTES) +struct props { + int eqmid; +}; + struct impl { HANDLE_LDAC_BT ldac; #ifdef ENABLE_LDAC_ABR @@ -317,7 +323,7 @@ static int codec_get_block_size(void *data) static int string_to_eqmid(const char * eqmid) { if (!strcmp("auto", eqmid)) - return LDACBT_EQMID_ABR; + return LDACBT_EQMID_AUTO; else if (!strcmp("hq", eqmid)) return LDACBT_EQMID_HQ; else if (!strcmp("sq", eqmid)) @@ -325,17 +331,37 @@ static int string_to_eqmid(const char * eqmid) else if (!strcmp("mq", eqmid)) return LDACBT_EQMID_MQ; else - return LDACBT_EQMID_ABR; + return LDACBT_EQMID_AUTO; +} + +static void *codec_init_props(const struct a2dp_codec *codec, const struct spa_dict *settings) +{ + struct props *p = calloc(1, sizeof(struct props)); + const char *str; + + if (p == NULL) + return NULL; + + if (settings == NULL || (str = spa_dict_lookup(settings, "bluez5.a2dp.ldac.quality")) == NULL) + str = "auto"; + + p->eqmid = string_to_eqmid(str); + return p; +} + +static void codec_clear_props(void *props) +{ + free(props); } static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, - const struct spa_dict *settings, size_t mtu) + void *props, size_t mtu) { struct impl *this; a2dp_ldac_t *conf = config; int res; - const char *str; + struct props *p = props; this = calloc(1, sizeof(struct impl)); if (this == NULL) @@ -351,12 +377,12 @@ static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, goto error_errno; #endif - if ((str = spa_dict_lookup(settings, "bluez5.a2dp.ldac.quality")) == NULL) - str = "auto"; - - if ((this->eqmid = string_to_eqmid(str)) == LDACBT_EQMID_ABR) { + if (p == NULL || p->eqmid == LDACBT_EQMID_AUTO) { this->eqmid = LDACBT_EQMID_SQ; this->enable_abr = true; + } else { + this->eqmid = p->eqmid; + this->enable_abr = false; } this->mtu = mtu; @@ -520,6 +546,8 @@ const struct a2dp_codec a2dp_codec_ldac = { .fill_caps = codec_fill_caps, .select_config = codec_select_config, .enum_config = codec_enum_config, + .init_props = codec_init_props, + .clear_props = codec_clear_props, .init = codec_init, .deinit = codec_deinit, .get_block_size = codec_get_block_size, diff --git a/spa/plugins/bluez5/a2dp-codec-sbc.c b/spa/plugins/bluez5/a2dp-codec-sbc.c index 6eca4a3cd..42a946087 100644 --- a/spa/plugins/bluez5/a2dp-codec-sbc.c +++ b/spa/plugins/bluez5/a2dp-codec-sbc.c @@ -427,7 +427,7 @@ static int codec_get_block_size(void *data) static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, - const struct spa_dict *settings, size_t mtu) + void *props, size_t mtu) { struct impl *this; a2dp_sbc_t *conf = config; diff --git a/spa/plugins/bluez5/a2dp-codecs.h b/spa/plugins/bluez5/a2dp-codecs.h index 79407ce70..14a64dcb7 100644 --- a/spa/plugins/bluez5/a2dp-codecs.h +++ b/spa/plugins/bluez5/a2dp-codecs.h @@ -356,11 +356,17 @@ struct a2dp_codec { int (*caps_preference_cmp) (const struct a2dp_codec *codec, const void *caps1, size_t caps1_size, const void *caps2, size_t caps2_size); + void *(*init_props) (const struct a2dp_codec *codec, 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); + int (*set_props) (void *props, const struct spa_pod *param); + void *(*init) (const struct a2dp_codec *codec, uint32_t flags, void *config, size_t config_size, - const struct spa_audio_info *info, const struct spa_dict *settings, size_t mtu); + const struct spa_audio_info *info, void *props, size_t mtu); void (*deinit) (void *data); - int (*update_settings) (void *data, const struct spa_dict *settings); + int (*update_props) (void *data, void *props); int (*get_block_size) (void *data); int (*get_num_blocks) (void *data); diff --git a/spa/plugins/bluez5/a2dp-sink.c b/spa/plugins/bluez5/a2dp-sink.c index 78365350b..81a6a94fc 100644 --- a/spa/plugins/bluez5/a2dp-sink.c +++ b/spa/plugins/bluez5/a2dp-sink.c @@ -128,6 +128,8 @@ struct impl { uint64_t last_error; const struct a2dp_codec *codec; + bool codec_props_changed; + void *codec_props; void *codec_data; struct spa_audio_info codec_format; @@ -167,7 +169,8 @@ static int impl_node_enum_params(void *object, int seq, struct spa_pod_builder b = { 0 }; uint8_t buffer[1024]; struct spa_result_node_params result; - uint32_t count = 0; + uint32_t count = 0, index_offset = 0; + bool enum_codec = false; spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); @@ -207,7 +210,8 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0, INT64_MIN, INT64_MAX)); break; default: - return 0; + enum_codec = true; + index_offset = 3; } break; } @@ -224,7 +228,8 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_latencyOffsetNsec, SPA_POD_Long(p->latency_offset)); break; default: - return 0; + enum_codec = true; + index_offset = 1; } break; } @@ -232,6 +237,16 @@ static int impl_node_enum_params(void *object, int seq, return -ENOENT; } + if (enum_codec) { + int res; + if (this->codec->enum_props == NULL || this->codec_props == NULL) + return 0; + else if ((res = this->codec->enum_props(this->codec_props, + this->transport->device->settings, + id, result.index - index_offset, &b, ¶m)) != 1) + return res; + } + if (spa_pod_filter(&b, &result.param, param, filter) < 0) goto next; @@ -340,7 +355,14 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, switch (id) { case SPA_PARAM_Props: { - if (apply_props(this, param) > 0) { + int res, codec_res = 0; + res = apply_props(this, param); + if (this->codec_props && this->codec->set_props) { + codec_res = this->codec->set_props(this->codec_props, param); + if (codec_res > 0) + this->codec_props_changed = true; + } + if (res > 0 || codec_res > 0) { this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; this->params[1].flags ^= SPA_PARAM_INFO_SERIAL; emit_node_info(this, false); @@ -361,6 +383,11 @@ static void update_num_blocks(struct impl *this) static int reset_buffer(struct impl *this) { + if (this->codec_props_changed && this->codec_props + && this->codec->update_props) { + this->codec->update_props(this->codec_data, this->codec_props); + this->codec_props_changed = false; + } this->frame_count = 0; this->buffer_used = this->codec->start_encode(this->codec_data, this->buffer, sizeof(this->buffer), @@ -708,7 +735,7 @@ static int do_start(struct impl *this) this->transport->configuration, this->transport->configuration_len, &port->current_format, - this->transport->device->settings, + this->codec_props, this->transport->write_mtu); if (this->codec_data == NULL) return -EIO; @@ -1317,6 +1344,8 @@ static int impl_clear(struct spa_handle *handle) if (this->codec_data) this->codec->deinit(this->codec_data); + if (this->codec_props && this->codec->clear_props) + this->codec->clear_props(this->codec_props); if (this->transport) spa_hook_remove(&this->transport_listener); spa_system_close(this->data_system, this->timerfd); @@ -1408,6 +1437,9 @@ impl_init(const struct spa_handle_factory *factory, return -EINVAL; } this->codec = this->transport->a2dp_codec; + if (this->codec->init_props != NULL) + this->codec_props = this->codec->init_props(this->codec, + this->transport->device->settings); spa_bt_transport_add_listener(this->transport, &this->transport_listener, &transport_events, this); diff --git a/spa/plugins/bluez5/a2dp-source.c b/spa/plugins/bluez5/a2dp-source.c index a6fc225de..2d5d7d42a 100644 --- a/spa/plugins/bluez5/a2dp-source.c +++ b/spa/plugins/bluez5/a2dp-source.c @@ -120,6 +120,8 @@ struct impl { struct spa_io_position *position; const struct a2dp_codec *codec; + bool codec_props_changed; + void *codec_props; void *codec_data; struct spa_audio_info codec_format; @@ -151,7 +153,8 @@ static int impl_node_enum_params(void *object, int seq, struct spa_pod_builder b = { 0 }; uint8_t buffer[1024]; struct spa_result_node_params result; - uint32_t count = 0; + uint32_t count = 0, index_offset = 0; + bool enum_codec = false; spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); @@ -184,7 +187,8 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->max_latency, 1, INT32_MAX)); break; default: - return 0; + enum_codec = true; + index_offset = 2; } break; } @@ -200,7 +204,8 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency)); break; default: - return 0; + enum_codec = true; + index_offset = 1; } break; } @@ -208,6 +213,17 @@ static int impl_node_enum_params(void *object, int seq, return -ENOENT; } + if (enum_codec) { + int res; + if (this->codec->enum_props == NULL || this->codec_props == NULL) + return 0; + else if ((res = this->codec->enum_props(this->codec_props, + this->transport->device->settings, + id, result.index - index_offset, + &b, ¶m)) != 1) + return res; + } + if (spa_pod_filter(&b, &result.param, param, filter) < 0) goto next; @@ -292,7 +308,14 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, switch (id) { case SPA_PARAM_Props: { - if (apply_props(this, param) > 0) { + int res, codec_res = 0; + res = apply_props(this, param); + if (this->codec_props && this->codec->set_props) { + codec_res = this->codec->set_props(this->codec_props, param); + if (codec_res > 0) + this->codec_props_changed = true; + } + if (res > 0 || codec_res > 0) { this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; this->params[1].flags ^= SPA_PARAM_INFO_SERIAL; emit_node_info(this, false); @@ -438,6 +461,12 @@ static void a2dp_on_ready_read(struct spa_source *source) } spa_log_trace(this->log, "read socket data %d", size_read); + if (this->codec_props_changed && this->codec_props + && this->codec->update_props) { + this->codec->update_props(this->codec_data, this->codec_props); + this->codec_props_changed = false; + } + /* decode */ decoded = decode_data(this, this->buffer_read, size_read, read_decoded, sizeof (read_decoded)); @@ -569,7 +598,7 @@ static int transport_start(struct impl *this) this->transport->configuration, this->transport->configuration_len, &port->current_format, - this->transport->device->settings, + this->codec_props, this->transport->read_mtu); if (this->codec_data == NULL) return -EIO; @@ -1186,6 +1215,8 @@ static int impl_clear(struct spa_handle *handle) struct impl *this = (struct impl *) handle; if (this->codec_data) this->codec->deinit(this->codec_data); + if (this->codec_props && this->codec->clear_props) + this->codec->clear_props(this->codec_props); return 0; } @@ -1282,6 +1313,9 @@ impl_init(const struct spa_handle_factory *factory, return -EINVAL; } this->codec = this->transport->a2dp_codec; + if (this->codec->init_props != NULL) + this->codec_props = this->codec->init_props(this->codec, + this->transport->device->settings); return 0; }