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.
This commit is contained in:
Wim Taymans 2020-12-03 18:05:57 +01:00
parent 67694a1491
commit 8bf0b7b4db
5 changed files with 203 additions and 49 deletions

View file

@ -113,6 +113,86 @@ static int codec_select_config(uint32_t flags, const void *caps, size_t caps_siz
return sizeof(conf); 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) static int codec_reduce_bitpool(void *data)
{ {
struct impl *this = 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); 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; struct impl *this = data;
size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload); 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;
this->mtu = mtu;
/* frame_count is only 4 bit number */ /* frame_count is only 4 bit number */
if (frame_count > 15) if (frame_count > 15)
@ -145,7 +223,8 @@ static int codec_get_block_size(void *data)
return this->codesize; 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; struct impl *this;
a2dp_ldac_t *conf = config; 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->media_subtype = SPA_MEDIA_SUBTYPE_raw;
info->info.raw.format = SPA_AUDIO_FORMAT_S16; info->info.raw.format = SPA_AUDIO_FORMAT_S16;
this->fmt = LDACBT_SMPL_FMT_S16; this->fmt = LDACBT_SMPL_FMT_S16;
this->mtu =mtu;
switch(conf->frequency) { switch(conf->frequency) {
case LDACBT_SAMPLING_FREQ_044100: case LDACBT_SAMPLING_FREQ_044100:
@ -308,6 +388,7 @@ struct a2dp_codec a2dp_codec_ldac = {
.description = "LDAC", .description = "LDAC",
.fill_caps = codec_fill_caps, .fill_caps = codec_fill_caps,
.select_config = codec_select_config, .select_config = codec_select_config,
.enum_config = codec_enum_config,
.init = codec_init, .init = codec_init,
.deinit = codec_deinit, .deinit = codec_deinit,
.get_block_size = codec_get_block_size, .get_block_size = codec_get_block_size,

View file

@ -43,6 +43,7 @@ struct impl {
struct rtp_header *header; struct rtp_header *header;
struct rtp_payload *payload; struct rtp_payload *payload;
size_t mtu;
int codesize; int codesize;
int frame_length; int frame_length;
@ -190,6 +191,75 @@ static int codec_set_bitpool(struct impl *this, int bitpool)
return this->sbc.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) static int codec_reduce_bitpool(void *data)
{ {
struct impl *this = 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); 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; struct impl *this = data;
size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload); 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 */ /* frame_count is only 4 bit number */
if (frame_count > 15) if (frame_count > 15)
@ -220,7 +290,7 @@ static int codec_get_block_size(void *data)
return this->codesize; 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; struct impl *this;
a2dp_sbc_t *conf = config; 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); sbc_init(&this->sbc, 0);
this->sbc.endian = SBC_LE; this->sbc.endian = SBC_LE;
this->mtu = mtu;
spa_zero(*info); spa_zero(*info);
info->media_type = SPA_MEDIA_TYPE_audio; info->media_type = SPA_MEDIA_TYPE_audio;
@ -417,6 +488,7 @@ struct a2dp_codec a2dp_codec_sbc = {
.description = "SBC", .description = "SBC",
.fill_caps = codec_fill_caps, .fill_caps = codec_fill_caps,
.select_config = codec_select_config, .select_config = codec_select_config,
.enum_config = codec_enum_config,
.init = codec_init, .init = codec_init,
.deinit = codec_deinit, .deinit = codec_deinit,
.get_block_size = codec_get_block_size, .get_block_size = codec_get_block_size,

View file

@ -29,6 +29,8 @@
#include <stddef.h> #include <stddef.h>
#include <spa/param/audio/format.h> #include <spa/param/audio/format.h>
#include <spa/pod/pod.h>
#include <spa/pod/builder.h>
#define A2DP_CODEC_SBC 0x00 #define A2DP_CODEC_SBC 0x00
#define A2DP_CODEC_MPEG12 0x01 #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 (*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, 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]); 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); void (*deinit) (void *data);
int (*get_block_size) (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, int (*start_encode) (void *data,
void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp); void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp);

View file

@ -597,6 +597,7 @@ static void a2dp_on_timeout(struct spa_source *source)
static int do_start(struct impl *this) static int do_start(struct impl *this)
{ {
int res, val; int res, val;
struct port *port;
socklen_t len; socklen_t len;
if (this->started) if (this->started)
@ -611,11 +612,20 @@ static int do_start(struct impl *this)
if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) if ((res = spa_bt_transport_acquire(this->transport, false)) < 0)
return res; 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->seqnum = 0;
this->block_size = this->codec->get_block_size(this->codec_data); this->block_size = this->codec->get_block_size(this->codec_data);
this->num_blocks = this->codec->get_num_blocks(this->codec_data, this->num_blocks = this->codec->get_num_blocks(this->codec_data);
this->transport->write_mtu);
spa_log_debug(this->log, NAME " %p: block_size %d num_blocks:%d", this, spa_log_debug(this->log, NAME " %p: block_size %d num_blocks:%d", this,
this->block_size, this->num_blocks); this->block_size, this->num_blocks);
@ -836,6 +846,7 @@ impl_node_port_enum_params(void *object, int seq,
uint8_t buffer[1024]; uint8_t buffer[1024];
struct spa_result_node_params result; struct spa_result_node_params result;
uint32_t count = 0; uint32_t count = 0;
int res;
spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(num != 0, -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: case SPA_PARAM_EnumFormat:
if (result.index > 0) if (result.index > 0)
return 0; return 0;
if (this->codec_data == NULL) if (this->codec == NULL)
return -EIO; return -EIO;
switch (this->transport->codec) { if ((res = this->codec->enum_config(
case A2DP_CODEC_SBC: this->transport->configuration,
param = spa_format_audio_raw_build(&b, id, &this->codec_format.info.raw); this->transport->configuration_len,
break; id, result.index, &b, &param)) != 1)
case A2DP_CODEC_MPEG24: return res;
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;
}
break; break;
case SPA_PARAM_Format: 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, this->timerfd = spa_system_timerfd_create(this->data_system,
CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); 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; return 0;
} }

View file

@ -492,12 +492,21 @@ stop:
static int transport_start(struct impl *this) static int transport_start(struct impl *this)
{ {
int res, val; int res, val;
struct port *port = &this->port;
spa_log_debug(this->log, NAME" %p: transport %p acquire", this, spa_log_debug(this->log, NAME" %p: transport %p acquire", this,
this->transport); this->transport);
if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) if ((res = spa_bt_transport_acquire(this->transport, false)) < 0)
return res; 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); val = fcntl(this->transport->fd, F_GETFL);
if (fcntl(this->transport->fd, F_SETFL, val | O_NONBLOCK) < 0) 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); 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]; uint8_t buffer[1024];
struct spa_result_node_params result; struct spa_result_node_params result;
uint32_t count = 0; uint32_t count = 0;
int res;
spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(num != 0, -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: case SPA_PARAM_EnumFormat:
if (result.index > 0) if (result.index > 0)
return 0; return 0;
if (this->codec_data == NULL) if (this->codec == NULL)
return -EIO; return -EIO;
switch (this->transport->codec) { if ((res = this->codec->enum_config(
case A2DP_CODEC_SBC: this->transport->configuration,
param = spa_format_audio_raw_build(&b, id, &this->codec_format.info.raw); this->transport->configuration_len,
break; id, result.index, &b, &param)) != 1)
case A2DP_CODEC_MPEG24: return res;
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;
}
break; break;
case SPA_PARAM_Format: case SPA_PARAM_Format:
@ -1210,11 +1212,6 @@ impl_init(const struct spa_handle_factory *factory,
spa_bt_transport_add_listener(this->transport, spa_bt_transport_add_listener(this->transport,
&this->transport_listener, &transport_events, this); &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; return 0;
} }