mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
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: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/440>
This commit is contained in:
parent
6044169763
commit
d61493640e
8 changed files with 69 additions and 7 deletions
|
|
@ -48,6 +48,9 @@ typedef struct pa_a2dp_codec {
|
||||||
/* True if codec is bi-directional and supports backchannel */
|
/* True if codec is bi-directional and supports backchannel */
|
||||||
bool support_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
|
/* Returns true if codec accepts capabilities, for_encoding is true when
|
||||||
* capabilities are used for encoding */
|
* capabilities are used for encoding */
|
||||||
bool (*can_accept_capabilities)(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding);
|
bool (*can_accept_capabilities)(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,28 @@
|
||||||
#include "a2dp-codec-gst.h"
|
#include "a2dp-codec-gst.h"
|
||||||
#include "rtp.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) {
|
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)
|
if (A2DP_GET_VENDOR_ID(capabilities->info) != vendor_id || A2DP_GET_CODEC_ID(capabilities->info) != codec_id)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -359,6 +381,7 @@ const pa_a2dp_codec pa_a2dp_codec_aptx = {
|
||||||
.description = "aptX",
|
.description = "aptX",
|
||||||
.id = { A2DP_CODEC_VENDOR, APTX_VENDOR_ID, APTX_CODEC_ID },
|
.id = { A2DP_CODEC_VENDOR, APTX_VENDOR_ID, APTX_CODEC_ID },
|
||||||
.support_backchannel = false,
|
.support_backchannel = false,
|
||||||
|
.can_be_supported = can_be_supported,
|
||||||
.can_accept_capabilities = can_accept_capabilities,
|
.can_accept_capabilities = can_accept_capabilities,
|
||||||
.choose_remote_endpoint = choose_remote_endpoint,
|
.choose_remote_endpoint = choose_remote_endpoint,
|
||||||
.fill_capabilities = fill_capabilities,
|
.fill_capabilities = fill_capabilities,
|
||||||
|
|
@ -379,6 +402,7 @@ const pa_a2dp_codec pa_a2dp_codec_aptx_hd = {
|
||||||
.description = "aptX HD",
|
.description = "aptX HD",
|
||||||
.id = { A2DP_CODEC_VENDOR, APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID },
|
.id = { A2DP_CODEC_VENDOR, APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID },
|
||||||
.support_backchannel = false,
|
.support_backchannel = false,
|
||||||
|
.can_be_supported = can_be_supported,
|
||||||
.can_accept_capabilities = can_accept_capabilities_hd,
|
.can_accept_capabilities = can_accept_capabilities_hd,
|
||||||
.choose_remote_endpoint = choose_remote_endpoint_hd,
|
.choose_remote_endpoint = choose_remote_endpoint_hd,
|
||||||
.fill_capabilities = fill_capabilities_hd,
|
.fill_capabilities = fill_capabilities_hd,
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
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;
|
struct gst_info *info = NULL;
|
||||||
GError *error = NULL;
|
|
||||||
bool ret;
|
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);
|
info = pa_xnew0(struct gst_info, 1);
|
||||||
pa_assert(info);
|
pa_assert(info);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,20 @@
|
||||||
#include "a2dp-codec-gst.h"
|
#include "a2dp-codec-gst.h"
|
||||||
#include "rtp.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) {
|
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)
|
if (A2DP_GET_VENDOR_ID(capabilities->info) != vendor_id || A2DP_GET_CODEC_ID(capabilities->info) != codec_id)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -276,6 +290,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_hq = {
|
||||||
.description = "LDAC (High Quality)",
|
.description = "LDAC (High Quality)",
|
||||||
.id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID },
|
.id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID },
|
||||||
.support_backchannel = false,
|
.support_backchannel = false,
|
||||||
|
.can_be_supported = can_be_supported,
|
||||||
.can_accept_capabilities = can_accept_capabilities,
|
.can_accept_capabilities = can_accept_capabilities,
|
||||||
.choose_remote_endpoint = choose_remote_endpoint,
|
.choose_remote_endpoint = choose_remote_endpoint,
|
||||||
.fill_capabilities = fill_capabilities,
|
.fill_capabilities = fill_capabilities,
|
||||||
|
|
@ -295,6 +310,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_sq = {
|
||||||
.description = "LDAC (Standard Quality)",
|
.description = "LDAC (Standard Quality)",
|
||||||
.id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID },
|
.id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID },
|
||||||
.support_backchannel = false,
|
.support_backchannel = false,
|
||||||
|
.can_be_supported = can_be_supported,
|
||||||
.can_accept_capabilities = can_accept_capabilities,
|
.can_accept_capabilities = can_accept_capabilities,
|
||||||
.choose_remote_endpoint = choose_remote_endpoint,
|
.choose_remote_endpoint = choose_remote_endpoint,
|
||||||
.fill_capabilities = fill_capabilities,
|
.fill_capabilities = fill_capabilities,
|
||||||
|
|
@ -314,6 +330,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_mq = {
|
||||||
.description = "LDAC (Mobile Quality)",
|
.description = "LDAC (Mobile Quality)",
|
||||||
.id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID },
|
.id = { A2DP_CODEC_VENDOR, LDAC_VENDOR_ID, LDAC_CODEC_ID },
|
||||||
.support_backchannel = false,
|
.support_backchannel = false,
|
||||||
|
.can_be_supported = can_be_supported,
|
||||||
.can_accept_capabilities = can_accept_capabilities,
|
.can_accept_capabilities = can_accept_capabilities,
|
||||||
.choose_remote_endpoint = choose_remote_endpoint,
|
.choose_remote_endpoint = choose_remote_endpoint,
|
||||||
.fill_capabilities = fill_capabilities,
|
.fill_capabilities = fill_capabilities,
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,10 @@ struct sbc_info {
|
||||||
uint8_t max_bitpool;
|
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) {
|
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;
|
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",
|
.description = "SBC",
|
||||||
.id = { A2DP_CODEC_SBC, 0, 0 },
|
.id = { A2DP_CODEC_SBC, 0, 0 },
|
||||||
.support_backchannel = false,
|
.support_backchannel = false,
|
||||||
|
.can_be_supported = can_be_supported,
|
||||||
.can_accept_capabilities = can_accept_capabilities,
|
.can_accept_capabilities = can_accept_capabilities,
|
||||||
.choose_remote_endpoint = choose_remote_endpoint,
|
.choose_remote_endpoint = choose_remote_endpoint,
|
||||||
.fill_capabilities = fill_capabilities,
|
.fill_capabilities = fill_capabilities,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@
|
||||||
|
|
||||||
#include <pulsecore/core.h>
|
#include <pulsecore/core.h>
|
||||||
#include <pulsecore/core-util.h>
|
#include <pulsecore/core-util.h>
|
||||||
|
#if defined(HAVE_GSTAPTX) || defined(HAVE_GSTLDAC)
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "a2dp-codec-util.h"
|
#include "a2dp-codec-util.h"
|
||||||
|
|
||||||
|
|
@ -72,3 +75,16 @@ const pa_a2dp_codec *pa_bluetooth_get_a2dp_codec(const char *name) {
|
||||||
|
|
||||||
return NULL;
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,7 @@ const pa_a2dp_codec *pa_bluetooth_a2dp_codec_iter(unsigned int i);
|
||||||
/* Get codec by name */
|
/* Get codec by name */
|
||||||
const pa_a2dp_codec *pa_bluetooth_get_a2dp_codec(const char *name);
|
const pa_a2dp_codec *pa_bluetooth_get_a2dp_codec(const char *name);
|
||||||
|
|
||||||
|
/* Initialise GStreamer */
|
||||||
|
void pa_bluetooth_a2dp_codec_gst_init(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2150,6 +2150,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe
|
||||||
const pa_a2dp_codec *a2dp_codec;
|
const pa_a2dp_codec *a2dp_codec;
|
||||||
char *endpoint;
|
char *endpoint;
|
||||||
|
|
||||||
|
pa_bluetooth_a2dp_codec_gst_init();
|
||||||
y = pa_xnew0(pa_bluetooth_discovery, 1);
|
y = pa_xnew0(pa_bluetooth_discovery, 1);
|
||||||
PA_REFCNT_INIT(y);
|
PA_REFCNT_INIT(y);
|
||||||
y->core = c;
|
y->core = c;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue