bluez5: tell the codec whether endpoint is sink or source

Add a flag A2DP_CODEC_FLAG_SINK to incidate a sink endpoint.

Also enum_config and caps_preference_cmp may need to know whether the
codec is being configured for SRC or SNK. Also add the flags argument to
init_props.

Bump codec API version.
This commit is contained in:
Pauli Virtanen 2022-05-21 13:18:38 +03:00 committed by Wim Taymans
parent 8d66b2b2f7
commit a8eb146d39
10 changed files with 63 additions and 34 deletions

View file

@ -170,7 +170,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
return sizeof(conf);
}
static int codec_enum_config(const struct a2dp_codec *codec,
static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
@ -289,7 +289,7 @@ static int codec_validate_config(const struct a2dp_codec *codec, uint32_t flags,
return 0;
}
static void *codec_init_props(const struct a2dp_codec *codec, const struct spa_dict *settings)
static void *codec_init_props(const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings)
{
struct props *p = calloc(1, sizeof(struct props));
const char *str;

View file

@ -218,7 +218,7 @@ static int codec_select_config_ll(const struct a2dp_codec *codec, uint32_t flags
return actual_conf_size;
}
static int codec_enum_config(const struct a2dp_codec *codec,
static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
@ -458,7 +458,7 @@ static int codec_decode(void *data,
* When connected as SRC to SNK, aptX-LL sink may send back mSBC data.
*/
static int msbc_enum_config(const struct a2dp_codec *codec,
static int msbc_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{

View file

@ -129,7 +129,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
return sizeof(conf);
}
static int codec_enum_config(const struct a2dp_codec *codec,
static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
@ -372,7 +372,7 @@ static SPA_UNUSED int codec_decode(void *data,
* When connected as SRC to SNK, FastStream sink may send back SBC data.
*/
static int duplex_enum_config(const struct a2dp_codec *codec,
static int duplex_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{

View file

@ -150,7 +150,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
return sizeof(conf);
}
static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void *caps1, size_t caps1_size,
static int codec_caps_preference_cmp(const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size,
const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info)
{
a2dp_lc3plus_hr_t conf1, conf2;
@ -160,7 +160,7 @@ static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void
/* 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);
res2 = codec->select_config(codec, 0, caps2, caps2_size, info, NULL, (uint8_t *)&conf2);
#define PREFER_EXPR(expr) \
do { \
@ -190,7 +190,7 @@ static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void
#undef PREFER_BOOL
}
static int codec_enum_config(const struct a2dp_codec *codec,
static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{

View file

@ -150,7 +150,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
return sizeof(conf);
}
static int codec_enum_config(const struct a2dp_codec *codec,
static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
@ -284,7 +284,7 @@ static int string_to_eqmid(const char * eqmid)
return LDACBT_EQMID_AUTO;
}
static void *codec_init_props(const struct a2dp_codec *codec, const struct spa_dict *settings)
static void *codec_init_props(const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings)
{
struct props *p = calloc(1, sizeof(struct props));
const char *str;

View file

@ -229,7 +229,7 @@ static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
return sizeof(conf);
}
static int codec_caps_preference_cmp(const struct a2dp_codec *codec, const void *caps1, size_t caps1_size,
static int codec_caps_preference_cmp(const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size,
const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info)
{
a2dp_sbc_t conf1, conf2;
@ -356,7 +356,7 @@ static int codec_set_bitpool(struct impl *this, int bitpool)
return this->sbc.bitpool;
}
static int codec_enum_config(const struct a2dp_codec *codec,
static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{

View file

@ -43,7 +43,7 @@
#define SPA_TYPE_INTERFACE_Bluez5CodecA2DP SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:A2DP:Private"
#define SPA_VERSION_BLUEZ5_CODEC_A2DP 1
#define SPA_VERSION_BLUEZ5_CODEC_A2DP 2
struct spa_bluez5_codec_a2dp {
struct spa_interface iface;
@ -62,6 +62,7 @@ extern const struct a2dp_codec * const * const codec_plugin_a2dp_codecs;
extern const char *codec_plugin_factory_name;
#endif
#define A2DP_CODEC_FLAG_SINK (1 << 0)
#define A2DP_CODEC_DEFAULT_RATE 48000
#define A2DP_CODEC_DEFAULT_CHANNELS 2
@ -97,7 +98,7 @@ struct a2dp_codec {
const void *caps, size_t caps_size,
const struct a2dp_codec_audio_info *info,
const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]);
int (*enum_config) (const struct a2dp_codec *codec,
int (*enum_config) (const struct a2dp_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);
int (*validate_config) (const struct a2dp_codec *codec, uint32_t flags,
@ -109,10 +110,10 @@ struct a2dp_codec {
* The caps handed in correspond to this codec_id, but are
* otherwise not checked beforehand.
*/
int (*caps_preference_cmp) (const struct a2dp_codec *codec, const void *caps1, size_t caps1_size,
int (*caps_preference_cmp) (const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size,
const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info);
void *(*init_props) (const struct a2dp_codec *codec, const struct spa_dict *settings);
void *(*init_props) (const struct a2dp_codec *codec, uint32_t flags, 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);

View file

@ -910,7 +910,8 @@ static int do_start(struct impl *this)
for (i = 0; i < size; i++)
spa_log_debug(this->log, " %d: %02x", i, conf[i]);
this->codec_data = this->codec->init(this->codec, 0,
this->codec_data = this->codec->init(this->codec,
this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0,
this->transport->configuration,
this->transport->configuration_len,
&port->current_format,
@ -1197,6 +1198,7 @@ impl_node_port_enum_params(void *object, int seq,
return -EIO;
if ((res = this->codec->enum_config(this->codec,
this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0,
this->transport->configuration,
this->transport->configuration_len,
id, result.index, &b, &param)) != 1)
@ -1726,6 +1728,7 @@ impl_init(const struct spa_handle_factory *factory,
if (this->codec->init_props != NULL)
this->codec_props = this->codec->init_props(this->codec,
this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0,
this->transport->device->settings);
reset_props(this, &this->props);

View file

@ -630,7 +630,8 @@ static int transport_start(struct impl *this)
this->transport_acquired = true;
this->codec_data = this->codec->init(this->codec, 0,
this->codec_data = this->codec->init(this->codec,
this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK,
this->transport->configuration,
this->transport->configuration_len,
&port->current_format,
@ -967,6 +968,7 @@ impl_node_port_enum_params(void *object, int seq,
return -EIO;
if ((res = this->codec->enum_config(this->codec,
this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK,
this->transport->configuration,
this->transport->configuration_len,
id, result.index, &b, &param)) != 1)
@ -1581,6 +1583,7 @@ impl_init(const struct spa_handle_factory *factory,
if (this->codec->init_props != NULL)
this->codec_props = this->codec->init_props(this->codec,
this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK,
this->transport->device->settings);
spa_bt_transport_add_listener(this->transport,

View file

@ -437,18 +437,21 @@ static int a2dp_codec_to_endpoint(const struct a2dp_codec *codec,
return 0;
}
static const struct a2dp_codec *a2dp_endpoint_to_codec(struct spa_bt_monitor *monitor, const char *endpoint)
static const struct a2dp_codec *a2dp_endpoint_to_codec(struct spa_bt_monitor *monitor, const char *endpoint, bool *sink)
{
const char *ep_name;
const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs;
int i;
if (spa_strstartswith(endpoint, A2DP_SINK_ENDPOINT "/"))
if (spa_strstartswith(endpoint, A2DP_SINK_ENDPOINT "/")) {
ep_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
else if (spa_strstartswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
*sink = true;
} else if (spa_strstartswith(endpoint, A2DP_SOURCE_ENDPOINT "/")) {
ep_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
else
*sink = false;
} else {
return NULL;
}
for (i = 0; a2dp_codecs[i]; i++) {
const struct a2dp_codec *codec = a2dp_codecs[i];
@ -486,6 +489,7 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
DBusError err;
int i, size, res;
const struct a2dp_codec *codec;
bool sink;
dbus_error_init(&err);
@ -501,14 +505,14 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
for (i = 0; i < size; i++)
spa_log_debug(monitor->log, " %d: %02x", i, cap[i]);
codec = a2dp_endpoint_to_codec(monitor, path);
codec = a2dp_endpoint_to_codec(monitor, path, &sink);
if (codec != NULL)
/* FIXME: We can't determine which device the SelectConfiguration()
* call is associated with, therefore device settings are not passed.
* This causes inconsistency with SelectConfiguration() triggered
* by codec switching.
*/
res = codec->select_config(codec, 0, cap, size, &monitor->default_audio_info, NULL, config);
res = codec->select_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, cap, size, &monitor->default_audio_info, NULL, config);
else
res = -ENOTSUP;
@ -2573,6 +2577,7 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s
DBusMessage *m;
DBusMessageIter iter, d;
int i;
bool sink;
/* Try setting configuration for current codec on current endpoint in list */
@ -2603,8 +2608,10 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s
if (sw->profile & SPA_BT_PROFILE_A2DP_SINK) {
local_endpoint_base = A2DP_SOURCE_ENDPOINT;
sink = false;
} else if (sw->profile & SPA_BT_PROFILE_A2DP_SOURCE) {
local_endpoint_base = A2DP_SINK_ENDPOINT;
sink = true;
} else {
spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: bad profile (%d), try next",
sw, sw->profile);
@ -2630,7 +2637,7 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s
}
}
res = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len,
res = codec->select_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, ep->capabilities, ep->capabilities_len,
&sw->device->monitor->default_audio_info,
sw->device->settings, config);
if (res < 0) {
@ -2878,6 +2885,7 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b)
const struct a2dp_codec *codec = *sw->codec_iter;
const char *path1 = *(char **)a, *path2 = *(char **)b;
struct spa_bt_remote_endpoint *ep1, *ep2;
uint32_t flags;
ep1 = device_remote_endpoint_find(sw->device, path1);
ep2 = device_remote_endpoint_find(sw->device, path2);
@ -2886,6 +2894,10 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b)
ep1 = NULL;
if (ep2 != NULL && (ep2->uuid == NULL || ep2->codec != codec->codec_id || ep2->capabilities == NULL))
ep2 = NULL;
if (ep1 && ep2 && !spa_streq(ep1->uuid, ep2->uuid)) {
ep1 = NULL;
ep2 = NULL;
}
if (ep1 == NULL && ep2 == NULL)
return 0;
@ -2894,7 +2906,9 @@ static int a2dp_codec_switch_cmp(const void *a, const void *b)
else if (ep2 == NULL)
return -1;
return codec->caps_preference_cmp(codec, ep1->capabilities, ep1->capabilities_len,
flags = spa_streq(ep1->uuid, SPA_BT_UUID_A2DP_SOURCE) ? A2DP_CODEC_FLAG_SINK : 0;
return codec->caps_preference_cmp(codec, flags, ep1->capabilities, ep1->capabilities_len,
ep2->capabilities, ep2->capabilities_len, &sw->device->monitor->default_audio_info);
}
@ -3031,6 +3045,7 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
struct spa_bt_transport *transport;
const struct a2dp_codec *codec;
int profile;
bool sink;
if (!dbus_message_has_signature(m, "oa{sv}")) {
spa_log_warn(monitor->log, "invalid SetConfiguration() signature");
@ -3039,7 +3054,7 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
endpoint = dbus_message_get_path(m);
profile = a2dp_endpoint_to_profile(endpoint);
codec = a2dp_endpoint_to_codec(monitor, endpoint);
codec = a2dp_endpoint_to_codec(monitor, endpoint, &sink);
if (codec == NULL) {
spa_log_warn(monitor->log, "unknown SetConfiguration() codec");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@ -3100,7 +3115,7 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
if (codec->validate_config) {
struct spa_audio_info info;
if (codec->validate_config(codec, 0,
if (codec->validate_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0,
transport->configuration, transport->configuration_len,
&info) < 0) {
spa_log_error(monitor->log, "invalid transport configuration");
@ -3290,12 +3305,15 @@ static int bluez_register_endpoint(struct spa_bt_monitor *monitor,
uint8_t caps[A2DP_MAX_CAPS_SIZE];
int ret, caps_size;
uint16_t codec_id = codec->codec_id;
bool sink;
ret = a2dp_codec_to_endpoint(codec, endpoint, &object_path);
if (ret < 0)
goto error;
ret = caps_size = codec->fill_caps(codec, 0, caps);
sink = spa_streq(endpoint, A2DP_SINK_ENDPOINT);
ret = caps_size = codec->fill_caps(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, caps);
if (ret < 0)
goto error;
@ -3501,11 +3519,11 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *
if (!is_a2dp_codec_enabled(monitor, codec))
continue;
caps_size = codec->fill_caps(codec, 0, caps);
if (caps_size < 0)
continue;
if (codec->decode != NULL) {
caps_size = codec->fill_caps(codec, A2DP_CODEC_FLAG_SINK, caps);
if (caps_size < 0)
continue;
ret = a2dp_codec_to_endpoint(codec, A2DP_SINK_ENDPOINT, &endpoint);
if (ret == 0) {
spa_log_info(monitor->log, "register A2DP sink codec %s: %s", a2dp_codecs[i]->name, endpoint);
@ -3516,6 +3534,10 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *
}
if (codec->encode != NULL) {
caps_size = codec->fill_caps(codec, 0, caps);
if (caps_size < 0)
continue;
ret = a2dp_codec_to_endpoint(codec, A2DP_SOURCE_ENDPOINT, &endpoint);
if (ret == 0) {
spa_log_info(monitor->log, "register A2DP source codec %s: %s", a2dp_codecs[i]->name, endpoint);