mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-02 09:01:46 -05:00
bluetooth: Modular API for A2DP codecs
This patch introduce new modular API for bluetooth A2DP codecs. Its benefits are: * bluez5-util and module-bluez5-device does not contain any codec specific code, they are codec independent. * For adding new A2DP codec it is needed just to adjust one table in a2dp-codec-util.c file. All codec specific functions are in separate codec file. * Support for backchannel (microphone voice). Some A2DP codecs (like FastStream or aptX Low Latency) are bi-directional and can be used for both music playback and audio call. * Support for more configurations per codec. This allows to implement low quality mode of some codec together with high quality. Current SBC codec implementation was moved from bluez5-util and module-bluez5-device to its own file and converted to this new A2DP API.
This commit is contained in:
parent
e8c4638f79
commit
106aa91477
8 changed files with 1127 additions and 622 deletions
|
|
@ -2,6 +2,7 @@
|
|||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2008-2013 João Paulo Rechi Vita
|
||||
Copyrigth 2018-2019 Pali Rohár <pali.rohar@gmail.com>
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
#include <pulsecore/refcnt.h>
|
||||
#include <pulsecore/shared.h>
|
||||
|
||||
#include "a2dp-codec-util.h"
|
||||
#include "a2dp-codecs.h"
|
||||
|
||||
#include "bluez5-util.h"
|
||||
|
|
@ -885,13 +887,19 @@ finish:
|
|||
pa_xfree(endpoint);
|
||||
}
|
||||
|
||||
static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) {
|
||||
static void register_endpoint(pa_bluetooth_discovery *y, const pa_a2dp_codec *a2dp_codec, const char *path, const char *endpoint, const char *uuid) {
|
||||
DBusMessage *m;
|
||||
DBusMessageIter i, d;
|
||||
uint8_t codec = 0;
|
||||
uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
|
||||
size_t capabilities_size;
|
||||
uint8_t codec_id;
|
||||
|
||||
pa_log_debug("Registering %s on adapter %s", endpoint, path);
|
||||
|
||||
codec_id = a2dp_codec->id.codec_id;
|
||||
capabilities_size = a2dp_codec->fill_capabilities(capabilities);
|
||||
pa_assert(capabilities_size != 0);
|
||||
|
||||
pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
|
||||
|
||||
dbus_message_iter_init_append(m, &i);
|
||||
|
|
@ -899,23 +907,8 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const
|
|||
dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
|
||||
DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
|
||||
pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
|
||||
pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec);
|
||||
|
||||
if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) || pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
|
||||
a2dp_sbc_t capabilities;
|
||||
|
||||
capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO |
|
||||
SBC_CHANNEL_MODE_JOINT_STEREO;
|
||||
capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 |
|
||||
SBC_SAMPLING_FREQ_48000;
|
||||
capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
|
||||
capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
|
||||
capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
|
||||
capabilities.min_bitpool = SBC_MIN_BITPOOL;
|
||||
capabilities.max_bitpool = 64;
|
||||
|
||||
pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
|
||||
}
|
||||
pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
|
||||
pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
|
||||
|
||||
dbus_message_iter_close_container(&i, &d);
|
||||
|
||||
|
|
@ -950,6 +943,7 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa
|
|||
|
||||
if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
|
||||
pa_bluetooth_adapter *a;
|
||||
unsigned a2dp_codec_i;
|
||||
|
||||
if ((a = pa_hashmap_get(y->adapters, path))) {
|
||||
pa_log_error("Found duplicated D-Bus path for adapter %s", path);
|
||||
|
|
@ -964,8 +958,20 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa
|
|||
if (!a->valid)
|
||||
return;
|
||||
|
||||
register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
|
||||
register_endpoint(y, path, A2DP_SINK_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK);
|
||||
/* Order is important. bluez prefers endpoints registered earlier.
|
||||
* And codec with higher number has higher priority. So iterate in reverse order. */
|
||||
for (a2dp_codec_i = pa_bluetooth_a2dp_codec_count(); a2dp_codec_i > 0; a2dp_codec_i--) {
|
||||
const pa_a2dp_codec *a2dp_codec = pa_bluetooth_a2dp_codec_iter(a2dp_codec_i-1);
|
||||
char *endpoint;
|
||||
|
||||
endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
|
||||
register_endpoint(y, a2dp_codec, path, endpoint, PA_BLUETOOTH_UUID_A2DP_SINK);
|
||||
pa_xfree(endpoint);
|
||||
|
||||
endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
|
||||
register_endpoint(y, a2dp_codec, path, endpoint, PA_BLUETOOTH_UUID_A2DP_SOURCE);
|
||||
pa_xfree(endpoint);
|
||||
}
|
||||
|
||||
} else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
|
||||
|
||||
|
|
@ -1257,48 +1263,6 @@ fail:
|
|||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
|
||||
/* These bitpool values were chosen based on the A2DP spec recommendation */
|
||||
switch (freq) {
|
||||
case SBC_SAMPLING_FREQ_16000:
|
||||
case SBC_SAMPLING_FREQ_32000:
|
||||
return 53;
|
||||
|
||||
case SBC_SAMPLING_FREQ_44100:
|
||||
|
||||
switch (mode) {
|
||||
case SBC_CHANNEL_MODE_MONO:
|
||||
case SBC_CHANNEL_MODE_DUAL_CHANNEL:
|
||||
return 31;
|
||||
|
||||
case SBC_CHANNEL_MODE_STEREO:
|
||||
case SBC_CHANNEL_MODE_JOINT_STEREO:
|
||||
return 53;
|
||||
}
|
||||
|
||||
pa_log_warn("Invalid channel mode %u", mode);
|
||||
return 53;
|
||||
|
||||
case SBC_SAMPLING_FREQ_48000:
|
||||
|
||||
switch (mode) {
|
||||
case SBC_CHANNEL_MODE_MONO:
|
||||
case SBC_CHANNEL_MODE_DUAL_CHANNEL:
|
||||
return 29;
|
||||
|
||||
case SBC_CHANNEL_MODE_STEREO:
|
||||
case SBC_CHANNEL_MODE_JOINT_STEREO:
|
||||
return 51;
|
||||
}
|
||||
|
||||
pa_log_warn("Invalid channel mode %u", mode);
|
||||
return 51;
|
||||
}
|
||||
|
||||
pa_log_warn("Invalid sampling freq %u", freq);
|
||||
return 53;
|
||||
}
|
||||
|
||||
const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
|
||||
switch(profile) {
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
|
||||
|
|
@ -1316,10 +1280,24 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static const pa_a2dp_codec *a2dp_endpoint_to_a2dp_codec(const char *endpoint) {
|
||||
const char *codec_name;
|
||||
|
||||
if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
|
||||
codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
|
||||
else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
|
||||
codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
|
||||
else
|
||||
return NULL;
|
||||
|
||||
return pa_bluetooth_get_a2dp_codec(codec_name);
|
||||
}
|
||||
|
||||
static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
|
||||
pa_bluetooth_discovery *y = userdata;
|
||||
pa_bluetooth_device *d;
|
||||
pa_bluetooth_transport *t;
|
||||
const pa_a2dp_codec *a2dp_codec = NULL;
|
||||
const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
|
||||
const uint8_t *config = NULL;
|
||||
int size = 0;
|
||||
|
|
@ -1345,6 +1323,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
|
|||
if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
|
||||
goto fail;
|
||||
|
||||
endpoint_path = dbus_message_get_path(m);
|
||||
|
||||
/* Read transport properties */
|
||||
while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
|
||||
const char *key;
|
||||
|
|
@ -1367,16 +1347,13 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
|
|||
|
||||
dbus_message_iter_get_basic(&value, &uuid);
|
||||
|
||||
endpoint_path = dbus_message_get_path(m);
|
||||
if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT)) {
|
||||
if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
|
||||
p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
|
||||
} else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) {
|
||||
if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
|
||||
p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
|
||||
}
|
||||
if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
|
||||
p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
|
||||
else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
|
||||
p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
|
||||
|
||||
if (p == PA_BLUETOOTH_PROFILE_OFF) {
|
||||
if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
|
||||
(pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
|
||||
pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -1389,7 +1366,6 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
|
|||
dbus_message_iter_get_basic(&value, &dev_path);
|
||||
} else if (pa_streq(key, "Configuration")) {
|
||||
DBusMessageIter array;
|
||||
a2dp_sbc_t *c;
|
||||
|
||||
if (var != DBUS_TYPE_ARRAY) {
|
||||
pa_log_error("Property %s of wrong type %c", key, (char)var);
|
||||
|
|
@ -1404,45 +1380,20 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
|
|||
}
|
||||
|
||||
dbus_message_iter_get_fixed_array(&array, &config, &size);
|
||||
if (size != sizeof(a2dp_sbc_t)) {
|
||||
pa_log_error("Configuration array of invalid size");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
c = (a2dp_sbc_t *) config;
|
||||
a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
|
||||
pa_assert(a2dp_codec);
|
||||
|
||||
if (c->frequency != SBC_SAMPLING_FREQ_16000 && c->frequency != SBC_SAMPLING_FREQ_32000 &&
|
||||
c->frequency != SBC_SAMPLING_FREQ_44100 && c->frequency != SBC_SAMPLING_FREQ_48000) {
|
||||
pa_log_error("Invalid sampling frequency in configuration");
|
||||
if (!a2dp_codec->is_configuration_valid(config, size))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (c->channel_mode != SBC_CHANNEL_MODE_MONO && c->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL &&
|
||||
c->channel_mode != SBC_CHANNEL_MODE_STEREO && c->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) {
|
||||
pa_log_error("Invalid channel mode in configuration");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (c->allocation_method != SBC_ALLOCATION_SNR && c->allocation_method != SBC_ALLOCATION_LOUDNESS) {
|
||||
pa_log_error("Invalid allocation method in configuration");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (c->subbands != SBC_SUBBANDS_4 && c->subbands != SBC_SUBBANDS_8) {
|
||||
pa_log_error("Invalid SBC subbands in configuration");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (c->block_length != SBC_BLOCK_LENGTH_4 && c->block_length != SBC_BLOCK_LENGTH_8 &&
|
||||
c->block_length != SBC_BLOCK_LENGTH_12 && c->block_length != SBC_BLOCK_LENGTH_16) {
|
||||
pa_log_error("Invalid block length in configuration");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&props);
|
||||
}
|
||||
|
||||
if (!a2dp_codec)
|
||||
goto fail2;
|
||||
|
||||
if ((d = pa_hashmap_get(y->devices, dev_path))) {
|
||||
if (!d->valid) {
|
||||
pa_log_error("Information about device %s is invalid", dev_path);
|
||||
|
|
@ -1466,6 +1417,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
|
|||
dbus_message_unref(r);
|
||||
|
||||
t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
|
||||
t->a2dp_codec = a2dp_codec;
|
||||
t->acquire = bluez5_transport_acquire_cb;
|
||||
t->release = bluez5_transport_release_cb;
|
||||
pa_bluetooth_transport_put(t);
|
||||
|
|
@ -1484,21 +1436,17 @@ fail2:
|
|||
|
||||
static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
|
||||
pa_bluetooth_discovery *y = userdata;
|
||||
a2dp_sbc_t *cap, config;
|
||||
uint8_t *pconf = (uint8_t *) &config;
|
||||
int i, size;
|
||||
const char *endpoint_path;
|
||||
uint8_t *cap;
|
||||
int size;
|
||||
const pa_a2dp_codec *a2dp_codec;
|
||||
uint8_t config[MAX_A2DP_CAPS_SIZE];
|
||||
uint8_t *config_ptr = config;
|
||||
size_t config_size;
|
||||
DBusMessage *r;
|
||||
DBusError err;
|
||||
|
||||
static const struct {
|
||||
uint32_t rate;
|
||||
uint8_t cap;
|
||||
} freq_table[] = {
|
||||
{ 16000U, SBC_SAMPLING_FREQ_16000 },
|
||||
{ 32000U, SBC_SAMPLING_FREQ_32000 },
|
||||
{ 44100U, SBC_SAMPLING_FREQ_44100 },
|
||||
{ 48000U, SBC_SAMPLING_FREQ_48000 }
|
||||
};
|
||||
endpoint_path = dbus_message_get_path(m);
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
|
|
@ -1508,101 +1456,15 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMess
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (size != sizeof(config)) {
|
||||
pa_log_error("Capabilities array has invalid size");
|
||||
goto fail;
|
||||
}
|
||||
a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
|
||||
pa_assert(a2dp_codec);
|
||||
|
||||
pa_zero(config);
|
||||
|
||||
/* Find the lowest freq that is at least as high as the requested sampling rate */
|
||||
for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
|
||||
if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
|
||||
config.frequency = freq_table[i].cap;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
|
||||
for (--i; i >= 0; i--) {
|
||||
if (cap->frequency & freq_table[i].cap) {
|
||||
config.frequency = freq_table[i].cap;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < 0) {
|
||||
pa_log_error("Not suitable sample rate");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
|
||||
|
||||
if (y->core->default_sample_spec.channels <= 1) {
|
||||
if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_MONO;
|
||||
else if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
|
||||
else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_STEREO;
|
||||
else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
|
||||
else {
|
||||
pa_log_error("No supported channel modes");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (y->core->default_sample_spec.channels >= 2) {
|
||||
if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
|
||||
else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_STEREO;
|
||||
else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
|
||||
else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
|
||||
config.channel_mode = SBC_CHANNEL_MODE_MONO;
|
||||
else {
|
||||
pa_log_error("No supported channel modes");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (cap->block_length & SBC_BLOCK_LENGTH_16)
|
||||
config.block_length = SBC_BLOCK_LENGTH_16;
|
||||
else if (cap->block_length & SBC_BLOCK_LENGTH_12)
|
||||
config.block_length = SBC_BLOCK_LENGTH_12;
|
||||
else if (cap->block_length & SBC_BLOCK_LENGTH_8)
|
||||
config.block_length = SBC_BLOCK_LENGTH_8;
|
||||
else if (cap->block_length & SBC_BLOCK_LENGTH_4)
|
||||
config.block_length = SBC_BLOCK_LENGTH_4;
|
||||
else {
|
||||
pa_log_error("No supported block lengths");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cap->subbands & SBC_SUBBANDS_8)
|
||||
config.subbands = SBC_SUBBANDS_8;
|
||||
else if (cap->subbands & SBC_SUBBANDS_4)
|
||||
config.subbands = SBC_SUBBANDS_4;
|
||||
else {
|
||||
pa_log_error("No supported subbands");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS)
|
||||
config.allocation_method = SBC_ALLOCATION_LOUDNESS;
|
||||
else if (cap->allocation_method & SBC_ALLOCATION_SNR)
|
||||
config.allocation_method = SBC_ALLOCATION_SNR;
|
||||
|
||||
config.min_bitpool = (uint8_t) PA_MAX(SBC_MIN_BITPOOL, cap->min_bitpool);
|
||||
config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
|
||||
|
||||
if (config.min_bitpool > config.max_bitpool)
|
||||
config_size = a2dp_codec->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
|
||||
if (config_size == 0)
|
||||
goto fail;
|
||||
|
||||
pa_assert_se(r = dbus_message_new_method_return(m));
|
||||
pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size, DBUS_TYPE_INVALID));
|
||||
pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &config_ptr, config_size, DBUS_TYPE_INVALID));
|
||||
|
||||
return r;
|
||||
|
||||
|
|
@ -1677,7 +1539,7 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
|
|||
|
||||
pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
|
||||
|
||||
if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT))
|
||||
if (!a2dp_endpoint_to_a2dp_codec(path))
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
|
||||
|
|
@ -1705,49 +1567,32 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
|
||||
static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
|
||||
static const DBusObjectPathVTable vtable_endpoint = {
|
||||
.message_function = endpoint_handler,
|
||||
};
|
||||
|
||||
pa_assert(y);
|
||||
pa_assert(endpoint);
|
||||
|
||||
switch(profile) {
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
|
||||
pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT,
|
||||
&vtable_endpoint, y));
|
||||
break;
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
|
||||
pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT,
|
||||
&vtable_endpoint, y));
|
||||
break;
|
||||
default:
|
||||
pa_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint,
|
||||
&vtable_endpoint, y));
|
||||
}
|
||||
|
||||
static void endpoint_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
|
||||
static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
|
||||
pa_assert(y);
|
||||
pa_assert(endpoint);
|
||||
|
||||
switch(profile) {
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
|
||||
dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
|
||||
break;
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
|
||||
dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
|
||||
break;
|
||||
default:
|
||||
pa_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
|
||||
}
|
||||
|
||||
pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend) {
|
||||
pa_bluetooth_discovery *y;
|
||||
DBusError err;
|
||||
DBusConnection *conn;
|
||||
unsigned i;
|
||||
unsigned i, count;
|
||||
const pa_a2dp_codec *a2dp_codec;
|
||||
char *endpoint;
|
||||
|
||||
y = pa_xnew0(pa_bluetooth_discovery, 1);
|
||||
PA_REFCNT_INIT(y);
|
||||
|
|
@ -1799,8 +1644,18 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe
|
|||
}
|
||||
y->matches_added = true;
|
||||
|
||||
endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
|
||||
endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
|
||||
count = pa_bluetooth_a2dp_codec_count();
|
||||
for (i = 0; i < count; i++) {
|
||||
a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
|
||||
|
||||
endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
|
||||
endpoint_init(y, endpoint);
|
||||
pa_xfree(endpoint);
|
||||
|
||||
endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
|
||||
endpoint_init(y, endpoint);
|
||||
pa_xfree(endpoint);
|
||||
}
|
||||
|
||||
get_managed_objects(y);
|
||||
|
||||
|
|
@ -1823,6 +1678,10 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
|
|||
}
|
||||
|
||||
void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
|
||||
unsigned i, count;
|
||||
const pa_a2dp_codec *a2dp_codec;
|
||||
char *endpoint;
|
||||
|
||||
pa_assert(y);
|
||||
pa_assert(PA_REFCNT_VALUE(y) > 0);
|
||||
|
||||
|
|
@ -1868,8 +1727,18 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
|
|||
if (y->filter_added)
|
||||
dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
|
||||
|
||||
endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
|
||||
endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
|
||||
count = pa_bluetooth_a2dp_codec_count();
|
||||
for (i = 0; i < count; i++) {
|
||||
a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
|
||||
|
||||
endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
|
||||
endpoint_done(y, endpoint);
|
||||
pa_xfree(endpoint);
|
||||
|
||||
endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
|
||||
endpoint_done(y, endpoint);
|
||||
pa_xfree(endpoint);
|
||||
}
|
||||
|
||||
pa_dbus_connection_unref(y->connection);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue