From d61493640e8ec82a188d33f6ae07a8dbe46c34c1 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Fri, 1 Jan 2021 19:32:37 +0530 Subject: [PATCH] bluetooth: Introduce a can_be_supported API for A2DP codecs This API internally checks if a requested codec can be supported on the system. This is especially required for codecs supported via GStreamer where the availability of a plugin decides if the said codec can be supported. This will be used to prevent registration of a codec which the remote endpoint device might be able to support, but, PulseAudio can't as the codec is not available on the system due to the absence of a plugin. We can also prevent listing or switching to an unavailable codec. Note that the codec negotiation happens with the bluez stack even before a device is connected. Because of this, we need to make sure that gst_init is called before checking for the availability of a plugin. Since module-bluez5-device gets loaded only after a connection to the device has been established, doing the gst_init in that or one of the bluetooth modules is not feasible. Part-of: --- src/modules/bluetooth/a2dp-codec-api.h | 3 +++ src/modules/bluetooth/a2dp-codec-aptx-gst.c | 24 +++++++++++++++++++++ src/modules/bluetooth/a2dp-codec-gst.c | 7 ------ src/modules/bluetooth/a2dp-codec-ldac-gst.c | 17 +++++++++++++++ src/modules/bluetooth/a2dp-codec-sbc.c | 5 +++++ src/modules/bluetooth/a2dp-codec-util.c | 16 ++++++++++++++ src/modules/bluetooth/a2dp-codec-util.h | 3 +++ src/modules/bluetooth/bluez5-util.c | 1 + 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/src/modules/bluetooth/a2dp-codec-api.h b/src/modules/bluetooth/a2dp-codec-api.h index a3123f4ca..61c66de88 100644 --- a/src/modules/bluetooth/a2dp-codec-api.h +++ b/src/modules/bluetooth/a2dp-codec-api.h @@ -48,6 +48,9 @@ typedef struct pa_a2dp_codec { /* True if codec is bi-directional and supports backchannel */ bool support_backchannel; + /* Returns true if the codec can be supported on the system */ + bool (*can_be_supported)(void); + /* Returns true if codec accepts capabilities, for_encoding is true when * capabilities are used for encoding */ bool (*can_accept_capabilities)(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding); diff --git a/src/modules/bluetooth/a2dp-codec-aptx-gst.c b/src/modules/bluetooth/a2dp-codec-aptx-gst.c index 11132162b..600ee99d3 100644 --- a/src/modules/bluetooth/a2dp-codec-aptx-gst.c +++ b/src/modules/bluetooth/a2dp-codec-aptx-gst.c @@ -33,6 +33,28 @@ #include "a2dp-codec-gst.h" #include "rtp.h" +static bool can_be_supported(void) { + GstElementFactory *element_factory; + + element_factory = gst_element_factory_find("openaptxenc"); + if (element_factory == NULL) { + pa_log_info("aptX encoder not found"); + return false; + } + + gst_object_unref(element_factory); + + element_factory = gst_element_factory_find("openaptxdec"); + if (element_factory == NULL) { + pa_log_info("aptX decoder not found"); + return false; + } + + gst_object_unref(element_factory); + + return true; +} + static bool can_accept_capabilities_common(const a2dp_aptx_t *capabilities, uint32_t vendor_id, uint16_t codec_id) { if (A2DP_GET_VENDOR_ID(capabilities->info) != vendor_id || A2DP_GET_CODEC_ID(capabilities->info) != codec_id) return false; @@ -359,6 +381,7 @@ const pa_a2dp_codec pa_a2dp_codec_aptx = { .description = "aptX", .id = { A2DP_CODEC_VENDOR, APTX_VENDOR_ID, APTX_CODEC_ID }, .support_backchannel = false, + .can_be_supported = can_be_supported, .can_accept_capabilities = can_accept_capabilities, .choose_remote_endpoint = choose_remote_endpoint, .fill_capabilities = fill_capabilities, @@ -379,6 +402,7 @@ const pa_a2dp_codec pa_a2dp_codec_aptx_hd = { .description = "aptX HD", .id = { A2DP_CODEC_VENDOR, APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID }, .support_backchannel = false, + .can_be_supported = can_be_supported, .can_accept_capabilities = can_accept_capabilities_hd, .choose_remote_endpoint = choose_remote_endpoint_hd, .fill_capabilities = fill_capabilities_hd, diff --git a/src/modules/bluetooth/a2dp-codec-gst.c b/src/modules/bluetooth/a2dp-codec-gst.c index c714c1d34..958f6ec20 100644 --- a/src/modules/bluetooth/a2dp-codec-gst.c +++ b/src/modules/bluetooth/a2dp-codec-gst.c @@ -575,15 +575,8 @@ fail: void *gst_codec_init(enum a2dp_codec_type codec_type, const uint8_t *config_buffer, uint8_t config_size, pa_sample_spec *ss) { struct gst_info *info = NULL; - GError *error = NULL; bool ret; - if (!gst_init_check(NULL, NULL, &error)) { - pa_log_error("Could not initialise GStreamer: %s", error->message); - g_error_free(error); - goto fail; - } - info = pa_xnew0(struct gst_info, 1); pa_assert(info); diff --git a/src/modules/bluetooth/a2dp-codec-ldac-gst.c b/src/modules/bluetooth/a2dp-codec-ldac-gst.c index 724275293..2f8e729d2 100644 --- a/src/modules/bluetooth/a2dp-codec-ldac-gst.c +++ b/src/modules/bluetooth/a2dp-codec-ldac-gst.c @@ -33,6 +33,20 @@ #include "a2dp-codec-gst.h" #include "rtp.h" +static bool can_be_supported(void) { + GstElementFactory *element_factory; + + element_factory = gst_element_factory_find("ldacenc"); + if (element_factory == NULL) { + pa_log_info("LDAC encoder not found"); + return false; + } + + gst_object_unref(element_factory); + + return true; +} + static bool can_accept_capabilities_common(const a2dp_ldac_t *capabilities, uint32_t vendor_id, uint16_t codec_id) { if (A2DP_GET_VENDOR_ID(capabilities->info) != vendor_id || A2DP_GET_CODEC_ID(capabilities->info) != codec_id) return false; @@ -276,6 +290,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_hq = { .description = "LDAC (High Quality)", .id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID }, .support_backchannel = false, + .can_be_supported = can_be_supported, .can_accept_capabilities = can_accept_capabilities, .choose_remote_endpoint = choose_remote_endpoint, .fill_capabilities = fill_capabilities, @@ -295,6 +310,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_sq = { .description = "LDAC (Standard Quality)", .id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID }, .support_backchannel = false, + .can_be_supported = can_be_supported, .can_accept_capabilities = can_accept_capabilities, .choose_remote_endpoint = choose_remote_endpoint, .fill_capabilities = fill_capabilities, @@ -314,6 +330,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_mq = { .description = "LDAC (Mobile Quality)", .id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID }, .support_backchannel = false, + .can_be_supported = can_be_supported, .can_accept_capabilities = can_accept_capabilities, .choose_remote_endpoint = choose_remote_endpoint, .fill_capabilities = fill_capabilities, diff --git a/src/modules/bluetooth/a2dp-codec-sbc.c b/src/modules/bluetooth/a2dp-codec-sbc.c index 89c647fbe..660f01100 100644 --- a/src/modules/bluetooth/a2dp-codec-sbc.c +++ b/src/modules/bluetooth/a2dp-codec-sbc.c @@ -53,6 +53,10 @@ struct sbc_info { uint8_t max_bitpool; }; +static bool can_be_supported(void) { + return true; +} + static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; @@ -666,6 +670,7 @@ const pa_a2dp_codec pa_a2dp_codec_sbc = { .description = "SBC", .id = { A2DP_CODEC_SBC, 0, 0 }, .support_backchannel = false, + .can_be_supported = can_be_supported, .can_accept_capabilities = can_accept_capabilities, .choose_remote_endpoint = choose_remote_endpoint, .fill_capabilities = fill_capabilities, diff --git a/src/modules/bluetooth/a2dp-codec-util.c b/src/modules/bluetooth/a2dp-codec-util.c index 28109988f..71482cdbf 100644 --- a/src/modules/bluetooth/a2dp-codec-util.c +++ b/src/modules/bluetooth/a2dp-codec-util.c @@ -23,6 +23,9 @@ #include #include +#if defined(HAVE_GSTAPTX) || defined(HAVE_GSTLDAC) +#include +#endif #include "a2dp-codec-util.h" @@ -72,3 +75,16 @@ const pa_a2dp_codec *pa_bluetooth_get_a2dp_codec(const char *name) { return NULL; } + +void pa_bluetooth_a2dp_codec_gst_init(void) { +#if defined(HAVE_GSTAPTX) || defined(HAVE_GSTLDAC) + GError *error = NULL; + + if (!gst_init_check(NULL, NULL, &error)) { + pa_log_error("Could not initialise GStreamer: %s", error->message); + g_error_free(error); + return; + } + pa_log_info("GStreamer initialisation done"); +#endif +} diff --git a/src/modules/bluetooth/a2dp-codec-util.h b/src/modules/bluetooth/a2dp-codec-util.h index 86f233a21..22fa8f65f 100644 --- a/src/modules/bluetooth/a2dp-codec-util.h +++ b/src/modules/bluetooth/a2dp-codec-util.h @@ -31,4 +31,7 @@ const pa_a2dp_codec *pa_bluetooth_a2dp_codec_iter(unsigned int i); /* Get codec by name */ const pa_a2dp_codec *pa_bluetooth_get_a2dp_codec(const char *name); +/* Initialise GStreamer */ +void pa_bluetooth_a2dp_codec_gst_init(void); + #endif diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index f46ad5c8e..76c53c21d 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -2150,6 +2150,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe const pa_a2dp_codec *a2dp_codec; char *endpoint; + pa_bluetooth_a2dp_codec_gst_init(); y = pa_xnew0(pa_bluetooth_discovery, 1); PA_REFCNT_INIT(y); y->core = c;