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 */
 | 
			
		||||
    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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,9 @@
 | 
			
		|||
 | 
			
		||||
#include <pulsecore/core.h>
 | 
			
		||||
#include <pulsecore/core-util.h>
 | 
			
		||||
#if defined(HAVE_GSTAPTX) || defined(HAVE_GSTLDAC)
 | 
			
		||||
#include <gst/gst.h>
 | 
			
		||||
#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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue