From 8bf0b7b4db4b974e7db3798ef0b64e0bd633e00e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 3 Dec 2020 18:05:57 +0100 Subject: [PATCH] a2dp: delay codec init to after acquire Some codecs need the MTU as a parameter so wait until we acquire with creating the codec context. Make some method to enumerate the parameters from the transport config and use that for the EnumFormat param. --- spa/plugins/bluez5/a2dp-codec-ldac.c | 91 ++++++++++++++++++++++++++-- spa/plugins/bluez5/a2dp-codec-sbc.c | 78 +++++++++++++++++++++++- spa/plugins/bluez5/a2dp-codecs.h | 9 ++- spa/plugins/bluez5/a2dp-sink.c | 39 ++++++------ spa/plugins/bluez5/a2dp-source.c | 35 +++++------ 5 files changed, 203 insertions(+), 49 deletions(-) diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index 3b07e09fb..a0b5d6056 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -113,6 +113,86 @@ static int codec_select_config(uint32_t flags, const void *caps, size_t caps_siz return sizeof(conf); } +static int codec_enum_config(const void *caps, size_t caps_size, uint32_t id, uint32_t idx, + struct spa_pod_builder *b, struct spa_pod **param) +{ + a2dp_sbc_t conf; + struct spa_pod_frame f[2]; + struct spa_pod_choice *choice; + uint32_t i = 0; + + if (caps_size < sizeof(conf)) + return -EINVAL; + + memcpy(&conf, caps, sizeof(conf)); + + if (idx > 0) + return 0; + + spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id); + spa_pod_builder_add(b, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16), + 0); + spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); + + spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0); + choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]); + i = 0; + if (conf.frequency & LDACBT_SAMPLING_FREQ_048000) { + if (i++ == 0) + spa_pod_builder_int(b, 48000); + spa_pod_builder_int(b, 48000); + } + if (conf.frequency & LDACBT_SAMPLING_FREQ_044100) { + if (i++ == 0) + spa_pod_builder_int(b, 44100); + spa_pod_builder_int(b, 44100); + } + if (conf.frequency & LDACBT_SAMPLING_FREQ_088200) { + if (i++ == 0) + spa_pod_builder_int(b, 88200); + spa_pod_builder_int(b, 88200); + } + if (conf.frequency & LDACBT_SAMPLING_FREQ_096000) { + if (i++ == 0) + spa_pod_builder_int(b, 96000); + spa_pod_builder_int(b, 96000); + } + if (conf.frequency & LDACBT_SAMPLING_FREQ_176400) { + if (i++ == 0) + spa_pod_builder_int(b, 176400); + spa_pod_builder_int(b, 176400); + } + if (conf.frequency & LDACBT_SAMPLING_FREQ_192000) { + if (i++ == 0) + spa_pod_builder_int(b, 192000); + spa_pod_builder_int(b, 192000); + } + if (i > 1) + choice->body.type = SPA_CHOICE_Enum; + spa_pod_builder_pop(b, &f[1]); + + if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO && + conf.channel_mode & (LDACBT_CHANNEL_MODE_STEREO | + LDACBT_CHANNEL_MODE_DUAL_CHANNEL)) { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2), + 0); + } else if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO) { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1), + 0); + } else { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2), + 0); + } + *param = spa_pod_builder_pop(b, &f[0]); + return 1; +} + static int codec_reduce_bitpool(void *data) { struct impl *this = data; @@ -125,13 +205,11 @@ static int codec_increase_bitpool(void *data) return ldacBT_alter_eqmid_priority(this->ldac, LDACBT_EQMID_INC_QUALITY); } -static int codec_get_num_blocks(void *data, size_t mtu) +static int codec_get_num_blocks(void *data) { struct impl *this = data; size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - size_t frame_count = (mtu - rtp_size) / this->frame_length; - - this->mtu = mtu; + size_t frame_count = (this->mtu - rtp_size) / this->frame_length; /* frame_count is only 4 bit number */ if (frame_count > 15) @@ -145,7 +223,8 @@ static int codec_get_block_size(void *data) return this->codesize; } -static void *codec_init(uint32_t flags, void *config, size_t config_len, struct spa_audio_info *info) +static void *codec_init(uint32_t flags, void *config, size_t config_len, struct spa_audio_info *info, + size_t mtu) { struct impl *this; a2dp_ldac_t *conf = config; @@ -166,6 +245,7 @@ static void *codec_init(uint32_t flags, void *config, size_t config_len, struct info->media_subtype = SPA_MEDIA_SUBTYPE_raw; info->info.raw.format = SPA_AUDIO_FORMAT_S16; this->fmt = LDACBT_SMPL_FMT_S16; + this->mtu =mtu; switch(conf->frequency) { case LDACBT_SAMPLING_FREQ_044100: @@ -308,6 +388,7 @@ struct a2dp_codec a2dp_codec_ldac = { .description = "LDAC", .fill_caps = codec_fill_caps, .select_config = codec_select_config, + .enum_config = codec_enum_config, .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 bce782ed2..3221e436d 100644 --- a/spa/plugins/bluez5/a2dp-codec-sbc.c +++ b/spa/plugins/bluez5/a2dp-codec-sbc.c @@ -43,6 +43,7 @@ struct impl { struct rtp_header *header; struct rtp_payload *payload; + size_t mtu; int codesize; int frame_length; @@ -190,6 +191,75 @@ static int codec_set_bitpool(struct impl *this, int bitpool) return this->sbc.bitpool; } +static int codec_enum_config(const void *caps, size_t caps_size, uint32_t id, uint32_t idx, + struct spa_pod_builder *b, struct spa_pod **param) +{ + a2dp_sbc_t conf; + struct spa_pod_frame f[2]; + struct spa_pod_choice *choice; + uint32_t i = 0; + + if (caps_size < sizeof(conf)) + return -EINVAL; + + memcpy(&conf, caps, sizeof(conf)); + + if (idx > 0) + return 0; + + spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id); + spa_pod_builder_add(b, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16), + 0); + spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); + + spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0); + choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]); + i = 0; + if (conf.frequency & SBC_SAMPLING_FREQ_48000) { + if (i++ == 0) + spa_pod_builder_int(b, 48000); + spa_pod_builder_int(b, 48000); + } + if (conf.frequency & SBC_SAMPLING_FREQ_44100) { + if (i++ == 0) + spa_pod_builder_int(b, 44100); + spa_pod_builder_int(b, 44100); + } + if (conf.frequency & SBC_SAMPLING_FREQ_32000) { + if (i++ == 0) + spa_pod_builder_int(b, 32000); + spa_pod_builder_int(b, 32000); + } + if (conf.frequency & SBC_SAMPLING_FREQ_16000) { + if (i++ == 0) + spa_pod_builder_int(b, 16000); + spa_pod_builder_int(b, 16000); + } + if (i > 1) + choice->body.type = SPA_CHOICE_Enum; + spa_pod_builder_pop(b, &f[1]); + + if (conf.channel_mode & SBC_CHANNEL_MODE_MONO && + conf.channel_mode & (SBC_CHANNEL_MODE_JOINT_STEREO | + SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_DUAL_CHANNEL)) { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2), + 0); + } else if (conf.channel_mode & SBC_CHANNEL_MODE_MONO) { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1), + 0); + } else { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2), 0); + } + *param = spa_pod_builder_pop(b, &f[0]); + return 1; +} + static int codec_reduce_bitpool(void *data) { struct impl *this = data; @@ -202,11 +272,11 @@ static int codec_increase_bitpool(void *data) return codec_set_bitpool(this, this->sbc.bitpool + 1); } -static int codec_get_num_blocks(void *data, size_t mtu) +static int codec_get_num_blocks(void *data) { struct impl *this = data; size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - size_t frame_count = (mtu - rtp_size) / this->frame_length; + size_t frame_count = (this->mtu - rtp_size) / this->frame_length; /* frame_count is only 4 bit number */ if (frame_count > 15) @@ -220,7 +290,7 @@ static int codec_get_block_size(void *data) return this->codesize; } -static void *codec_init(uint32_t flags, void *config, size_t config_len, struct spa_audio_info *info) +static void *codec_init(uint32_t flags, void *config, size_t config_len, struct spa_audio_info *info, size_t mtu) { struct impl *this; a2dp_sbc_t *conf = config; @@ -234,6 +304,7 @@ static void *codec_init(uint32_t flags, void *config, size_t config_len, struct sbc_init(&this->sbc, 0); this->sbc.endian = SBC_LE; + this->mtu = mtu; spa_zero(*info); info->media_type = SPA_MEDIA_TYPE_audio; @@ -417,6 +488,7 @@ struct a2dp_codec a2dp_codec_sbc = { .description = "SBC", .fill_caps = codec_fill_caps, .select_config = codec_select_config, + .enum_config = codec_enum_config, .init = codec_init, .deinit = codec_deinit, .get_block_size = codec_get_block_size, diff --git a/spa/plugins/bluez5/a2dp-codecs.h b/spa/plugins/bluez5/a2dp-codecs.h index ec8a2d4cd..0a6c95557 100644 --- a/spa/plugins/bluez5/a2dp-codecs.h +++ b/spa/plugins/bluez5/a2dp-codecs.h @@ -29,6 +29,8 @@ #include #include +#include +#include #define A2DP_CODEC_SBC 0x00 #define A2DP_CODEC_MPEG12 0x01 @@ -321,12 +323,15 @@ struct a2dp_codec { int (*fill_caps) (uint32_t flags, uint8_t caps[A2DP_MAX_CAPS_SIZE]); int (*select_config) (uint32_t flags, const void *caps, size_t caps_size, const struct spa_audio_info *info, uint8_t config[A2DP_MAX_CAPS_SIZE]); + int (*enum_config) (const void *caps, size_t caps_size, uint32_t id, uint32_t idx, + struct spa_pod_builder *builder, struct spa_pod **param); - void *(*init) (uint32_t flags, void *config, size_t config_size, struct spa_audio_info *info); + void *(*init) (uint32_t flags, void *config, size_t config_size, + struct spa_audio_info *info, size_t mtu); void (*deinit) (void *data); int (*get_block_size) (void *data); - int (*get_num_blocks) (void *data, size_t mtu); + int (*get_num_blocks) (void *data); int (*start_encode) (void *data, void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp); diff --git a/spa/plugins/bluez5/a2dp-sink.c b/spa/plugins/bluez5/a2dp-sink.c index 2b3e1b1d7..20499281b 100644 --- a/spa/plugins/bluez5/a2dp-sink.c +++ b/spa/plugins/bluez5/a2dp-sink.c @@ -597,6 +597,7 @@ static void a2dp_on_timeout(struct spa_source *source) static int do_start(struct impl *this) { int res, val; + struct port *port; socklen_t len; if (this->started) @@ -611,11 +612,20 @@ static int do_start(struct impl *this) if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) return res; + port = &this->port; + + this->codec_data = this->codec->init(0, + this->transport->configuration, + this->transport->configuration_len, + &port->current_format, + this->transport->write_mtu); + if (this->codec_data == NULL) + return -EIO; + this->seqnum = 0; this->block_size = this->codec->get_block_size(this->codec_data); - this->num_blocks = this->codec->get_num_blocks(this->codec_data, - this->transport->write_mtu); + this->num_blocks = this->codec->get_num_blocks(this->codec_data); spa_log_debug(this->log, NAME " %p: block_size %d num_blocks:%d", this, this->block_size, this->num_blocks); @@ -836,6 +846,7 @@ impl_node_port_enum_params(void *object, int seq, uint8_t buffer[1024]; struct spa_result_node_params result; uint32_t count = 0; + int res; spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); @@ -854,22 +865,14 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_EnumFormat: if (result.index > 0) return 0; - if (this->codec_data == NULL) + if (this->codec == NULL) return -EIO; - switch (this->transport->codec) { - case A2DP_CODEC_SBC: - param = spa_format_audio_raw_build(&b, id, &this->codec_format.info.raw); - break; - case A2DP_CODEC_MPEG24: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Format, id, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_aac)); - break; - default: - return -EIO; - } + if ((res = this->codec->enum_config( + this->transport->configuration, + this->transport->configuration_len, + id, result.index, &b, ¶m)) != 1) + return res; break; case SPA_PARAM_Format: @@ -1264,10 +1267,6 @@ impl_init(const struct spa_handle_factory *factory, this->timerfd = spa_system_timerfd_create(this->data_system, CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - this->codec_data = this->codec->init(0, this->transport->configuration, - this->transport->configuration_len, - &this->codec_format); - return 0; } diff --git a/spa/plugins/bluez5/a2dp-source.c b/spa/plugins/bluez5/a2dp-source.c index 77f3a7f3d..04e2e7779 100644 --- a/spa/plugins/bluez5/a2dp-source.c +++ b/spa/plugins/bluez5/a2dp-source.c @@ -492,12 +492,21 @@ stop: static int transport_start(struct impl *this) { int res, val; + struct port *port = &this->port; spa_log_debug(this->log, NAME" %p: transport %p acquire", this, this->transport); if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) return res; + this->codec_data = this->codec->init(0, + this->transport->configuration, + this->transport->configuration_len, + &port->current_format, + this->transport->read_mtu); + if (this->codec_data == NULL) + return -EIO; + val = fcntl(this->transport->fd, F_GETFL); if (fcntl(this->transport->fd, F_SETFL, val | O_NONBLOCK) < 0) spa_log_warn(this->log, NAME" %p: fcntl %u %m", this, val | O_NONBLOCK); @@ -718,6 +727,7 @@ impl_node_port_enum_params(void *object, int seq, uint8_t buffer[1024]; struct spa_result_node_params result; uint32_t count = 0; + int res; spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); @@ -736,22 +746,14 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_EnumFormat: if (result.index > 0) return 0; - if (this->codec_data == NULL) + if (this->codec == NULL) return -EIO; - switch (this->transport->codec) { - case A2DP_CODEC_SBC: - param = spa_format_audio_raw_build(&b, id, &this->codec_format.info.raw); - break; - case A2DP_CODEC_MPEG24: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Format, id, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_aac)); - break; - default: - return -EIO; - } + if ((res = this->codec->enum_config( + this->transport->configuration, + this->transport->configuration_len, + id, result.index, &b, ¶m)) != 1) + return res; break; case SPA_PARAM_Format: @@ -1210,11 +1212,6 @@ impl_init(const struct spa_handle_factory *factory, spa_bt_transport_add_listener(this->transport, &this->transport_listener, &transport_events, this); - this->codec_data = this->codec->init(0, - this->transport->configuration, - this->transport->configuration_len, - &this->codec_format); - return 0; }