mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
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:
parent
67694a1491
commit
8bf0b7b4db
5 changed files with 203 additions and 49 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
#include <stddef.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_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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue