mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
bluez5: make codecs configurable
Make the list of supported codecs available. Register all supported codecs endpoints Find the codec from the endpoint name Put the codec on the transport for the sink to find.
This commit is contained in:
parent
e18c4d76dc
commit
53ee5ce72a
5 changed files with 97 additions and 124 deletions
|
|
@ -50,34 +50,6 @@ const a2dp_mpeg_t bluez_a2dp_mpeg = {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ENABLE_AAC
|
|
||||||
const a2dp_aac_t bluez_a2dp_aac = {
|
|
||||||
.object_type =
|
|
||||||
/* NOTE: AAC Long Term Prediction and AAC Scalable are
|
|
||||||
* not supported by the FDK-AAC library. */
|
|
||||||
AAC_OBJECT_TYPE_MPEG2_AAC_LC |
|
|
||||||
AAC_OBJECT_TYPE_MPEG4_AAC_LC,
|
|
||||||
AAC_INIT_FREQUENCY(
|
|
||||||
AAC_SAMPLING_FREQ_8000 |
|
|
||||||
AAC_SAMPLING_FREQ_11025 |
|
|
||||||
AAC_SAMPLING_FREQ_12000 |
|
|
||||||
AAC_SAMPLING_FREQ_16000 |
|
|
||||||
AAC_SAMPLING_FREQ_22050 |
|
|
||||||
AAC_SAMPLING_FREQ_24000 |
|
|
||||||
AAC_SAMPLING_FREQ_32000 |
|
|
||||||
AAC_SAMPLING_FREQ_44100 |
|
|
||||||
AAC_SAMPLING_FREQ_48000 |
|
|
||||||
AAC_SAMPLING_FREQ_64000 |
|
|
||||||
AAC_SAMPLING_FREQ_88200 |
|
|
||||||
AAC_SAMPLING_FREQ_96000)
|
|
||||||
.channels =
|
|
||||||
AAC_CHANNELS_1 |
|
|
||||||
AAC_CHANNELS_2,
|
|
||||||
.vbr = 1,
|
|
||||||
AAC_INIT_BITRATE(0xFFFF)
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_APTX
|
#if ENABLE_APTX
|
||||||
const a2dp_aptx_t bluez_a2dp_aptx = {
|
const a2dp_aptx_t bluez_a2dp_aptx = {
|
||||||
.info.vendor_id = APTX_VENDOR_ID,
|
.info.vendor_id = APTX_VENDOR_ID,
|
||||||
|
|
@ -95,3 +67,29 @@ const a2dp_aptx_t bluez_a2dp_aptx = {
|
||||||
APTX_SAMPLING_FREQ_48000,
|
APTX_SAMPLING_FREQ_48000,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern struct a2dp_codec a2dp_codec_sbc;
|
||||||
|
#if ENABLE_AAC
|
||||||
|
extern struct a2dp_codec a2dp_codec_aac;
|
||||||
|
#endif
|
||||||
|
#if ENABLE_MP3
|
||||||
|
extern struct a2dp_codec a2dp_codec_mpeg;
|
||||||
|
#endif
|
||||||
|
#if ENABLE_APTX
|
||||||
|
extern struct a2dp_codec a2dp_codec_aptx;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct a2dp_codec *a2dp_codec_list[] = {
|
||||||
|
&a2dp_codec_sbc,
|
||||||
|
#if ENABLE_AAC
|
||||||
|
&a2dp_codec_aac,
|
||||||
|
#endif
|
||||||
|
#if ENABLE_MP3
|
||||||
|
&a2dp_codec_mpeg,
|
||||||
|
#endif
|
||||||
|
#if ENABLE_APTX
|
||||||
|
&a2dp_codec_aptx,
|
||||||
|
#endif
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
const struct a2dp_codec **a2dp_codecs = a2dp_codec_list;
|
||||||
|
|
|
||||||
|
|
@ -325,14 +325,6 @@ struct a2dp_codec {
|
||||||
int (*increase_bitpool) (void *data);
|
int (*increase_bitpool) (void *data);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct a2dp_codec a2dp_codec_sbc;
|
extern const struct a2dp_codec **a2dp_codecs;
|
||||||
extern struct a2dp_codec a2dp_codec_aac;
|
|
||||||
|
|
||||||
#if ENABLE_MP3
|
|
||||||
extern const a2dp_mpeg_t bluez_a2dp_mpeg;
|
|
||||||
#endif
|
|
||||||
#if ENABLE_APTX
|
|
||||||
extern const a2dp_aptx_t bluez_a2dp_aptx;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ struct impl {
|
||||||
uint64_t next_time;
|
uint64_t next_time;
|
||||||
uint64_t last_error;
|
uint64_t last_error;
|
||||||
|
|
||||||
struct a2dp_codec *codec;
|
const struct a2dp_codec *codec;
|
||||||
void *codec_data;
|
void *codec_data;
|
||||||
struct spa_audio_info codec_format;
|
struct spa_audio_info codec_format;
|
||||||
|
|
||||||
|
|
@ -1251,13 +1251,18 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
spa_log_error(this->log, "a transport is needed");
|
spa_log_error(this->log, "a transport is needed");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (this->transport->a2dp_codec == NULL) {
|
||||||
|
spa_log_error(this->log, "a transport codec is needed");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
this->codec = this->transport->a2dp_codec;
|
||||||
|
|
||||||
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->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 = &a2dp_codec_sbc;
|
|
||||||
this->codec_data = this->codec->init(0, this->transport->configuration,
|
this->codec_data = this->codec->init(0, this->transport->configuration,
|
||||||
this->transport->configuration_len,
|
this->transport->configuration_len,
|
||||||
&this->codec_format);
|
&this->codec_format);
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,26 @@ static inline void add_dict(struct spa_pod_builder *builder, const char *key, co
|
||||||
spa_pod_builder_string(builder, val);
|
spa_pod_builder_string(builder, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct a2dp_codec *a2dp_endpoint_to_codec(const char *endpoint)
|
||||||
|
{
|
||||||
|
const char *codec_name;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (strstr(endpoint, A2DP_SINK_ENDPOINT "/") == endpoint)
|
||||||
|
codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
|
||||||
|
else if (strstr(endpoint, A2DP_SOURCE_ENDPOINT "/") == endpoint)
|
||||||
|
codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 0; a2dp_codecs[i]; i++) {
|
||||||
|
const struct a2dp_codec *codec = a2dp_codecs[i];
|
||||||
|
if (strcmp(codec->name, codec_name) == 0)
|
||||||
|
return codec;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata)
|
static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata)
|
||||||
{
|
{
|
||||||
struct spa_bt_monitor *monitor = userdata;
|
struct spa_bt_monitor *monitor = userdata;
|
||||||
|
|
@ -93,7 +113,7 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
|
||||||
DBusMessage *r;
|
DBusMessage *r;
|
||||||
DBusError err;
|
DBusError err;
|
||||||
int size, res;
|
int size, res;
|
||||||
struct a2dp_codec *codec;
|
const struct a2dp_codec *codec;
|
||||||
|
|
||||||
dbus_error_init(&err);
|
dbus_error_init(&err);
|
||||||
|
|
||||||
|
|
@ -107,16 +127,12 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
|
||||||
}
|
}
|
||||||
spa_log_info(monitor->log, "%p: select conf %d", monitor, size);
|
spa_log_info(monitor->log, "%p: select conf %d", monitor, size);
|
||||||
|
|
||||||
if (strstr(path, "/A2DP/SBC/") == path) {
|
codec = a2dp_endpoint_to_codec(path);
|
||||||
codec = &a2dp_codec_sbc;
|
if (codec != NULL)
|
||||||
} else if (strstr(path, "/A2DP/MPEG24/") == path) {
|
res = codec->select_config(0, cap, size, NULL, config);
|
||||||
codec = &a2dp_codec_aac;
|
else
|
||||||
} else
|
|
||||||
codec = NULL;
|
|
||||||
if (codec == NULL)
|
|
||||||
res = -ENOTSUP;
|
res = -ENOTSUP;
|
||||||
|
|
||||||
res = codec->select_config(0, cap, size, NULL, config);
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if ((r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments",
|
if ((r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments",
|
||||||
"Unable to select configuration")) == NULL)
|
"Unable to select configuration")) == NULL)
|
||||||
|
|
@ -901,16 +917,24 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
|
||||||
const char *path, DBusMessage *m, void *userdata)
|
const char *path, DBusMessage *m, void *userdata)
|
||||||
{
|
{
|
||||||
struct spa_bt_monitor *monitor = userdata;
|
struct spa_bt_monitor *monitor = userdata;
|
||||||
const char *transport_path;
|
const char *transport_path, *endpoint;
|
||||||
DBusMessageIter it[2];
|
DBusMessageIter it[2];
|
||||||
DBusMessage *r;
|
DBusMessage *r;
|
||||||
struct spa_bt_transport *transport;
|
struct spa_bt_transport *transport;
|
||||||
bool is_new = false;
|
bool is_new = false;
|
||||||
|
const struct a2dp_codec *codec;
|
||||||
|
|
||||||
if (!dbus_message_has_signature(m, "oa{sv}")) {
|
if (!dbus_message_has_signature(m, "oa{sv}")) {
|
||||||
spa_log_warn(monitor->log, "invalid SetConfiguration() signature");
|
spa_log_warn(monitor->log, "invalid SetConfiguration() signature");
|
||||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
}
|
}
|
||||||
|
endpoint = dbus_message_get_path(m);
|
||||||
|
|
||||||
|
codec = a2dp_endpoint_to_codec(endpoint);
|
||||||
|
if (codec == NULL) {
|
||||||
|
spa_log_warn(monitor->log, "unknown SetConfiguration() codec");
|
||||||
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
dbus_message_iter_init(m, &it[0]);
|
dbus_message_iter_init(m, &it[0]);
|
||||||
dbus_message_iter_get_basic(&it[0], &transport_path);
|
dbus_message_iter_get_basic(&it[0], &transport_path);
|
||||||
|
|
@ -928,6 +952,7 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
|
||||||
spa_bt_transport_set_implementation(transport, &transport_impl, transport);
|
spa_bt_transport_set_implementation(transport, &transport_impl, transport);
|
||||||
}
|
}
|
||||||
transport_update_props(transport, &it[1], NULL);
|
transport_update_props(transport, &it[1], NULL);
|
||||||
|
transport->a2dp_codec = codec;
|
||||||
|
|
||||||
if (transport->device == NULL) {
|
if (transport->device == NULL) {
|
||||||
spa_log_warn(monitor->log, "no device found for transport");
|
spa_log_warn(monitor->log, "no device found for transport");
|
||||||
|
|
@ -1071,14 +1096,9 @@ static void register_endpoint_reply(DBusPendingCall *pending, void *user_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int register_a2dp_endpoint(struct spa_bt_monitor *monitor,
|
static int register_a2dp_endpoint(struct spa_bt_monitor *monitor,
|
||||||
const char *path,
|
const char *path, const char *uuid,
|
||||||
const char *uuid,
|
const struct a2dp_codec *codec, const char *endpoint)
|
||||||
enum spa_bt_profile profile,
|
|
||||||
uint16_t codec,
|
|
||||||
const void *configuration,
|
|
||||||
size_t configuration_size)
|
|
||||||
{
|
{
|
||||||
const char *profile_path;
|
|
||||||
char *object_path, *str;
|
char *object_path, *str;
|
||||||
const DBusObjectPathVTable vtable_endpoint = {
|
const DBusObjectPathVTable vtable_endpoint = {
|
||||||
.message_function = endpoint_handler,
|
.message_function = endpoint_handler,
|
||||||
|
|
@ -1086,42 +1106,16 @@ static int register_a2dp_endpoint(struct spa_bt_monitor *monitor,
|
||||||
DBusMessage *m;
|
DBusMessage *m;
|
||||||
DBusMessageIter it[5];
|
DBusMessageIter it[5];
|
||||||
DBusPendingCall *call;
|
DBusPendingCall *call;
|
||||||
|
uint8_t caps[A2DP_MAX_CAPS_SIZE];
|
||||||
|
uint8_t *pcaps = (uint8_t *) caps;
|
||||||
|
int caps_size;
|
||||||
|
uint16_t codec_id = codec->codec_id;
|
||||||
|
|
||||||
switch (profile) {
|
caps_size = codec->fill_caps(0, caps);
|
||||||
case SPA_BT_PROFILE_A2DP_SOURCE:
|
if (caps_size < 0)
|
||||||
switch (codec) {
|
return caps_size;
|
||||||
case A2DP_CODEC_SBC:
|
|
||||||
profile_path = "/A2DP/SBC/Source";
|
|
||||||
break;
|
|
||||||
case A2DP_CODEC_MPEG24:
|
|
||||||
/* We don't support MPEG24 for now */
|
|
||||||
return -ENOTSUP;
|
|
||||||
/* profile_path = "/A2DP/MPEG24/Source";
|
|
||||||
break; */
|
|
||||||
default:
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SPA_BT_PROFILE_A2DP_SINK:
|
|
||||||
switch (codec) {
|
|
||||||
case A2DP_CODEC_SBC:
|
|
||||||
profile_path = "/A2DP/SBC/Sink";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case A2DP_CODEC_MPEG24:
|
object_path = spa_aprintf("%s/%s", endpoint, codec->name);
|
||||||
/* We don't support MPEG24 for now */
|
|
||||||
return -ENOTSUP;
|
|
||||||
/* profile_path = "/A2DP/MPEG24/Sink";
|
|
||||||
break; */
|
|
||||||
default:
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
object_path = spa_aprintf("%s/%d", profile_path, monitor->count++);
|
|
||||||
if (object_path == NULL)
|
if (object_path == NULL)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
|
@ -1156,7 +1150,7 @@ static int register_a2dp_endpoint(struct spa_bt_monitor *monitor,
|
||||||
str = "Codec";
|
str = "Codec";
|
||||||
dbus_message_iter_append_basic(&it[2], DBUS_TYPE_STRING, &str);
|
dbus_message_iter_append_basic(&it[2], DBUS_TYPE_STRING, &str);
|
||||||
dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "y", &it[3]);
|
dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "y", &it[3]);
|
||||||
dbus_message_iter_append_basic(&it[3], DBUS_TYPE_BYTE, &codec);
|
dbus_message_iter_append_basic(&it[3], DBUS_TYPE_BYTE, &codec_id);
|
||||||
dbus_message_iter_close_container(&it[2], &it[3]);
|
dbus_message_iter_close_container(&it[2], &it[3]);
|
||||||
dbus_message_iter_close_container(&it[1], &it[2]);
|
dbus_message_iter_close_container(&it[1], &it[2]);
|
||||||
|
|
||||||
|
|
@ -1166,7 +1160,7 @@ static int register_a2dp_endpoint(struct spa_bt_monitor *monitor,
|
||||||
dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "ay", &it[3]);
|
dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "ay", &it[3]);
|
||||||
dbus_message_iter_open_container(&it[3], DBUS_TYPE_ARRAY, "y", &it[4]);
|
dbus_message_iter_open_container(&it[3], DBUS_TYPE_ARRAY, "y", &it[4]);
|
||||||
dbus_message_iter_append_fixed_array (&it[4], DBUS_TYPE_BYTE,
|
dbus_message_iter_append_fixed_array (&it[4], DBUS_TYPE_BYTE,
|
||||||
&configuration, configuration_size);
|
&pcaps, caps_size);
|
||||||
dbus_message_iter_close_container(&it[3], &it[4]);
|
dbus_message_iter_close_container(&it[3], &it[4]);
|
||||||
dbus_message_iter_close_container(&it[2], &it[3]);
|
dbus_message_iter_close_container(&it[2], &it[3]);
|
||||||
dbus_message_iter_close_container(&it[1], &it[2]);
|
dbus_message_iter_close_container(&it[1], &it[2]);
|
||||||
|
|
@ -1183,39 +1177,19 @@ static int register_a2dp_endpoint(struct spa_bt_monitor *monitor,
|
||||||
static int adapter_register_endpoints(struct spa_bt_adapter *a)
|
static int adapter_register_endpoints(struct spa_bt_adapter *a)
|
||||||
{
|
{
|
||||||
struct spa_bt_monitor *monitor = a->monitor;
|
struct spa_bt_monitor *monitor = a->monitor;
|
||||||
struct a2dp_codec *codec;
|
int i;
|
||||||
uint8_t caps[A2DP_MAX_CAPS_SIZE];
|
|
||||||
int caps_size;
|
|
||||||
|
|
||||||
/* We don't support MPEG24 for now */
|
for (i = 0; a2dp_codecs[i]; i++) {
|
||||||
/*
|
const struct a2dp_codec *codec = a2dp_codecs[i];
|
||||||
register_a2dp_endpoint(monitor, a->path,
|
register_a2dp_endpoint(monitor, a->path,
|
||||||
SPA_BT_UUID_A2DP_SOURCE,
|
SPA_BT_UUID_A2DP_SOURCE,
|
||||||
SPA_BT_PROFILE_A2DP_SOURCE,
|
codec,
|
||||||
A2DP_CODEC_MPEG24,
|
A2DP_SOURCE_ENDPOINT);
|
||||||
&bluez_a2dp_aac, sizeof(bluez_a2dp_aac));
|
register_a2dp_endpoint(monitor, a->path,
|
||||||
register_a2dp_endpoint(monitor, a->path,
|
SPA_BT_UUID_A2DP_SINK,
|
||||||
SPA_BT_UUID_A2DP_SINK,
|
codec,
|
||||||
SPA_BT_PROFILE_A2DP_SINK,
|
A2DP_SINK_ENDPOINT);
|
||||||
A2DP_CODEC_MPEG24,
|
}
|
||||||
&bluez_a2dp_aac, sizeof(bluez_a2dp_aac));
|
|
||||||
*/
|
|
||||||
|
|
||||||
codec = &a2dp_codec_sbc;
|
|
||||||
caps_size = codec->fill_caps(0, caps);
|
|
||||||
spa_log_info(monitor->log, "register %s config_size:%d",
|
|
||||||
codec->name, caps_size);
|
|
||||||
|
|
||||||
register_a2dp_endpoint(monitor, a->path,
|
|
||||||
SPA_BT_UUID_A2DP_SOURCE,
|
|
||||||
SPA_BT_PROFILE_A2DP_SOURCE,
|
|
||||||
codec->codec_id,
|
|
||||||
caps, caps_size);
|
|
||||||
register_a2dp_endpoint(monitor, a->path,
|
|
||||||
SPA_BT_UUID_A2DP_SINK,
|
|
||||||
SPA_BT_PROFILE_A2DP_SINK,
|
|
||||||
codec->codec_id,
|
|
||||||
caps, caps_size);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,9 @@ extern "C" {
|
||||||
#define HFP_AUDIO_CODEC_CVSD 0x01
|
#define HFP_AUDIO_CODEC_CVSD 0x01
|
||||||
#define HFP_AUDIO_CODEC_MSBC 0x02
|
#define HFP_AUDIO_CODEC_MSBC 0x02
|
||||||
|
|
||||||
|
#define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink"
|
||||||
|
#define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
|
||||||
|
|
||||||
enum spa_bt_profile {
|
enum spa_bt_profile {
|
||||||
SPA_BT_PROFILE_NULL = 0,
|
SPA_BT_PROFILE_NULL = 0,
|
||||||
SPA_BT_PROFILE_A2DP_SINK = (1 << 0),
|
SPA_BT_PROFILE_A2DP_SINK = (1 << 0),
|
||||||
|
|
@ -243,6 +246,7 @@ struct spa_bt_transport {
|
||||||
struct spa_list device_link;
|
struct spa_list device_link;
|
||||||
enum spa_bt_profile profile;
|
enum spa_bt_profile profile;
|
||||||
enum spa_bt_transport_state state;
|
enum spa_bt_transport_state state;
|
||||||
|
const struct a2dp_codec *a2dp_codec;
|
||||||
int codec;
|
int codec;
|
||||||
void *configuration;
|
void *configuration;
|
||||||
int configuration_len;
|
int configuration_len;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue