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