mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-10-29 05:40:27 -04:00 
			
		
		
		
	bluez5: bap: allow configuring server locations/context
Add configuration options for the BAP/PACS sink and source endpoint location (= channel positions) and context settings. Although BlueZ associates these with individual endpoints, in the PACS spec they are actually device-global, so configure directly in monitor settings.
This commit is contained in:
		
							parent
							
								
									a4148c80b4
								
							
						
					
					
						commit
						081116906d
					
				
					 4 changed files with 138 additions and 53 deletions
				
			
		|  | @ -963,6 +963,26 @@ Maximum number of octets supported per codec frame for the LC3 codec (default: 4 | |||
| @PAR@ monitor-prop  bluez5.bap-server-capabilities.max_frames		# integer | ||||
| Maximum number of codec frames supported per SDU for the LC3 codec (default: 2). | ||||
| 
 | ||||
| @PAR@ monitor-prop  bluez5.bap-server-capabilities.sink.locations		# JSON or integer | ||||
| Sink audio locations of the server, as channel positions or PACS bitmask. | ||||
| Example: `FL,FR` | ||||
| 
 | ||||
| @PAR@ monitor-prop  bluez5.bap-server-capabilities.sink.contexts		# integer | ||||
| Available sink contexts PACS bitmask of the the server. | ||||
| 
 | ||||
| @PAR@ monitor-prop  bluez5.bap-server-capabilities.sink.supported-contexts		# integer | ||||
| Supported sink contexts PACS bitmask of the the server. | ||||
| 
 | ||||
| @PAR@ monitor-prop  bluez5.bap-server-capabilities.source.locations		# JSON or integer | ||||
| Source audio locations of the server, as channel positions or PACS bitmask. | ||||
| Example: `FL,FR` | ||||
| 
 | ||||
| @PAR@ monitor-prop  bluez5.bap-server-capabilities.source.contexts		# integer | ||||
| Available source contexts PACS bitmask of the the server. | ||||
| 
 | ||||
| @PAR@ monitor-prop  bluez5.bap-server-capabilities.source.supported-contexts		# integer | ||||
| Supported source contexts PACS bitmask of the the server. | ||||
| 
 | ||||
| ## Device properties | ||||
| 
 | ||||
| @PAR@ device-prop  bluez5.auto-connect   # boolean | ||||
|  |  | |||
|  | @ -5,6 +5,8 @@ | |||
| #ifndef SPA_BLUEZ5_BAP_CODEC_CAPS_H_ | ||||
| #define SPA_BLUEZ5_BAP_CODEC_CAPS_H_ | ||||
| 
 | ||||
| #include <spa/param/audio/format.h> | ||||
| 
 | ||||
| #define BAP_CODEC_LC3           0x06 | ||||
| 
 | ||||
| #define LC3_TYPE_FREQ           0x01 | ||||
|  | @ -175,4 +177,39 @@ struct bap_codec_qos_full { | |||
| 	struct bap_codec_qos qos; | ||||
| }; | ||||
| 
 | ||||
| static const struct { | ||||
| 	uint32_t bit; | ||||
| 	enum spa_audio_channel channel; | ||||
| } bap_channel_bits[] = { | ||||
| 	{ BAP_CHANNEL_MONO, SPA_AUDIO_CHANNEL_MONO }, | ||||
| 	{ BAP_CHANNEL_FL,   SPA_AUDIO_CHANNEL_FL }, | ||||
| 	{ BAP_CHANNEL_FR,   SPA_AUDIO_CHANNEL_FR }, | ||||
| 	{ BAP_CHANNEL_FC,   SPA_AUDIO_CHANNEL_FC }, | ||||
| 	{ BAP_CHANNEL_LFE,  SPA_AUDIO_CHANNEL_LFE }, | ||||
| 	{ BAP_CHANNEL_BL,   SPA_AUDIO_CHANNEL_RL }, | ||||
| 	{ BAP_CHANNEL_BR,   SPA_AUDIO_CHANNEL_RR }, | ||||
| 	{ BAP_CHANNEL_FLC,  SPA_AUDIO_CHANNEL_FLC }, | ||||
| 	{ BAP_CHANNEL_FRC,  SPA_AUDIO_CHANNEL_FRC }, | ||||
| 	{ BAP_CHANNEL_BC,   SPA_AUDIO_CHANNEL_BC }, | ||||
| 	{ BAP_CHANNEL_LFE2, SPA_AUDIO_CHANNEL_LFE2 }, | ||||
| 	{ BAP_CHANNEL_SL,   SPA_AUDIO_CHANNEL_SL }, | ||||
| 	{ BAP_CHANNEL_SR,   SPA_AUDIO_CHANNEL_SR }, | ||||
| 	{ BAP_CHANNEL_TFL,  SPA_AUDIO_CHANNEL_TFL }, | ||||
| 	{ BAP_CHANNEL_TFR,  SPA_AUDIO_CHANNEL_TFR }, | ||||
| 	{ BAP_CHANNEL_TFC,  SPA_AUDIO_CHANNEL_TFC }, | ||||
| 	{ BAP_CHANNEL_TC,   SPA_AUDIO_CHANNEL_TC }, | ||||
| 	{ BAP_CHANNEL_TBL,  SPA_AUDIO_CHANNEL_TRL }, | ||||
| 	{ BAP_CHANNEL_TBR,  SPA_AUDIO_CHANNEL_TRR }, | ||||
| 	{ BAP_CHANNEL_TSL,  SPA_AUDIO_CHANNEL_TSL }, | ||||
| 	{ BAP_CHANNEL_TSR,  SPA_AUDIO_CHANNEL_TSR }, | ||||
| 	{ BAP_CHANNEL_TBC,  SPA_AUDIO_CHANNEL_TRC }, | ||||
| 	{ BAP_CHANNEL_BFC,  SPA_AUDIO_CHANNEL_BC }, | ||||
| 	{ BAP_CHANNEL_BFL,  SPA_AUDIO_CHANNEL_BLC }, | ||||
| 	{ BAP_CHANNEL_BFR,  SPA_AUDIO_CHANNEL_BRC }, | ||||
| 	{ BAP_CHANNEL_FLW,  SPA_AUDIO_CHANNEL_FLW }, | ||||
| 	{ BAP_CHANNEL_FRW,  SPA_AUDIO_CHANNEL_FRW }, | ||||
| 	{ BAP_CHANNEL_LS,   SPA_AUDIO_CHANNEL_SL }, /* is it the right mapping? */ | ||||
| 	{ BAP_CHANNEL_RS,   SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */ | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -81,41 +81,6 @@ typedef struct { | |||
| 	unsigned int priority; | ||||
| } bap_lc3_t; | ||||
| 
 | ||||
| static const struct { | ||||
| 	uint32_t bit; | ||||
| 	enum spa_audio_channel channel; | ||||
| } channel_bits[] = { | ||||
| 	{ BAP_CHANNEL_MONO, SPA_AUDIO_CHANNEL_MONO }, | ||||
| 	{ BAP_CHANNEL_FL,   SPA_AUDIO_CHANNEL_FL }, | ||||
| 	{ BAP_CHANNEL_FR,   SPA_AUDIO_CHANNEL_FR }, | ||||
| 	{ BAP_CHANNEL_FC,   SPA_AUDIO_CHANNEL_FC }, | ||||
| 	{ BAP_CHANNEL_LFE,  SPA_AUDIO_CHANNEL_LFE }, | ||||
| 	{ BAP_CHANNEL_BL,   SPA_AUDIO_CHANNEL_RL }, | ||||
| 	{ BAP_CHANNEL_BR,   SPA_AUDIO_CHANNEL_RR }, | ||||
| 	{ BAP_CHANNEL_FLC,  SPA_AUDIO_CHANNEL_FLC }, | ||||
| 	{ BAP_CHANNEL_FRC,  SPA_AUDIO_CHANNEL_FRC }, | ||||
| 	{ BAP_CHANNEL_BC,   SPA_AUDIO_CHANNEL_BC }, | ||||
| 	{ BAP_CHANNEL_LFE2, SPA_AUDIO_CHANNEL_LFE2 }, | ||||
| 	{ BAP_CHANNEL_SL,   SPA_AUDIO_CHANNEL_SL }, | ||||
| 	{ BAP_CHANNEL_SR,   SPA_AUDIO_CHANNEL_SR }, | ||||
| 	{ BAP_CHANNEL_TFL,  SPA_AUDIO_CHANNEL_TFL }, | ||||
| 	{ BAP_CHANNEL_TFR,  SPA_AUDIO_CHANNEL_TFR }, | ||||
| 	{ BAP_CHANNEL_TFC,  SPA_AUDIO_CHANNEL_TFC }, | ||||
| 	{ BAP_CHANNEL_TC,   SPA_AUDIO_CHANNEL_TC }, | ||||
| 	{ BAP_CHANNEL_TBL,  SPA_AUDIO_CHANNEL_TRL }, | ||||
| 	{ BAP_CHANNEL_TBR,  SPA_AUDIO_CHANNEL_TRR }, | ||||
| 	{ BAP_CHANNEL_TSL,  SPA_AUDIO_CHANNEL_TSL }, | ||||
| 	{ BAP_CHANNEL_TSR,  SPA_AUDIO_CHANNEL_TSR }, | ||||
| 	{ BAP_CHANNEL_TBC,  SPA_AUDIO_CHANNEL_TRC }, | ||||
| 	{ BAP_CHANNEL_BFC,  SPA_AUDIO_CHANNEL_BC }, | ||||
| 	{ BAP_CHANNEL_BFL,  SPA_AUDIO_CHANNEL_BLC }, | ||||
| 	{ BAP_CHANNEL_BFR,  SPA_AUDIO_CHANNEL_BRC }, | ||||
| 	{ BAP_CHANNEL_FLW,  SPA_AUDIO_CHANNEL_FLW }, | ||||
| 	{ BAP_CHANNEL_FRW,  SPA_AUDIO_CHANNEL_FRW }, | ||||
| 	{ BAP_CHANNEL_LS,   SPA_AUDIO_CHANNEL_SL }, /* is it the right mapping? */ | ||||
| 	{ BAP_CHANNEL_RS,   SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */ | ||||
| }; | ||||
| 
 | ||||
| #define BAP_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \ | ||||
| 	((struct bap_qos){ .name = (name_), .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \ | ||||
| 			 .framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_),	\ | ||||
|  | @ -569,9 +534,9 @@ static int select_channels(uint8_t channel_counts, uint32_t locations, uint32_t | |||
| 		return -1; | ||||
| 
 | ||||
| 	*allocation = 0; | ||||
| 	for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i) { | ||||
| 		if (locations & channel_bits[i].bit) { | ||||
| 			*allocation |= channel_bits[i].bit; | ||||
| 	for (i = 0; i < SPA_N_ELEMENTS(bap_channel_bits); ++i) { | ||||
| 		if (locations & bap_channel_bits[i].bit) { | ||||
| 			*allocation |= bap_channel_bits[i].bit; | ||||
| 			--num; | ||||
| 			if (num == 0) | ||||
| 				break; | ||||
|  | @ -944,9 +909,9 @@ static uint8_t channels_to_positions(uint32_t channels, uint32_t *position) | |||
| 	} else { | ||||
| 		unsigned int i; | ||||
| 
 | ||||
| 		for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i) | ||||
| 			if (channels & channel_bits[i].bit) | ||||
| 				position[n_positions++] = channel_bits[i].channel; | ||||
| 		for (i = 0; i < SPA_N_ELEMENTS(bap_channel_bits); ++i) | ||||
| 			if (channels & bap_channel_bits[i].bit) | ||||
| 				position[n_positions++] = bap_channel_bits[i].channel; | ||||
| 	} | ||||
| 
 | ||||
| 	if (n_positions != n_channels) | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ | |||
| #include <spa/utils/string.h> | ||||
| #include <spa/utils/json.h> | ||||
| #include <spa-private/dbus-helpers.h> | ||||
| #include <spa/param/audio/raw-json.h> | ||||
| 
 | ||||
| #include "config.h" | ||||
| #include "codec-loader.h" | ||||
|  | @ -122,6 +123,13 @@ struct spa_bt_monitor { | |||
| 
 | ||||
| 	struct spa_list bcast_source_config_list; | ||||
| 
 | ||||
| 	uint32_t bap_sink_locations; | ||||
| 	uint32_t bap_sink_contexts; | ||||
| 	uint32_t bap_sink_supported_contexts; | ||||
| 	uint32_t bap_source_locations; | ||||
| 	uint32_t bap_source_contexts; | ||||
| 	uint32_t bap_source_supported_contexts; | ||||
| 
 | ||||
| 	struct spa_bt_quirks *quirks; | ||||
| 
 | ||||
| #define MAX_SETTINGS 128 | ||||
|  | @ -5006,7 +5014,7 @@ out: | |||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void append_media_object(DBusMessageIter *iter, const char *endpoint, | ||||
| static void append_media_object(struct spa_bt_monitor *monitor, DBusMessageIter *iter, const char *endpoint, | ||||
| 		const char *uuid, uint8_t codec_id, uint8_t *caps, size_t caps_size) | ||||
| { | ||||
| 	const char *interface_name = BLUEZ_MEDIA_ENDPOINT_INTERFACE; | ||||
|  | @ -5033,19 +5041,24 @@ static void append_media_object(DBusMessageIter *iter, const char *endpoint, | |||
| 	} | ||||
| 	if (spa_bt_profile_from_uuid(uuid) & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) { | ||||
| 		dbus_uint32_t locations; | ||||
| 		dbus_uint16_t supported_context, context; | ||||
| 		dbus_uint16_t supported_contexts, contexts; | ||||
| 
 | ||||
| 		locations = BAP_CHANNEL_ALL; | ||||
| 		if (spa_bt_profile_from_uuid(uuid) & SPA_BT_PROFILE_BAP_SINK) { | ||||
| 			supported_context = context = BAP_CONTEXT_ALL; | ||||
| 			locations = monitor->bap_sink_locations; | ||||
| 			contexts = monitor->bap_sink_contexts; | ||||
| 			supported_contexts = monitor->bap_sink_supported_contexts; | ||||
| 		} else { | ||||
| 			supported_context = context = (BAP_CONTEXT_UNSPECIFIED | BAP_CONTEXT_CONVERSATIONAL | | ||||
| 					BAP_CONTEXT_MEDIA | BAP_CONTEXT_GAME); | ||||
| 			locations = monitor->bap_source_locations; | ||||
| 			contexts = monitor->bap_source_contexts; | ||||
| 			supported_contexts = monitor->bap_source_supported_contexts; | ||||
| 		} | ||||
| 
 | ||||
| 		spa_log_debug(monitor->log, "BAP endpoint %s locations:0x%x contexts:0x%x supported-contexs:0x%x", | ||||
| 				endpoint, locations, contexts, supported_contexts); | ||||
| 
 | ||||
| 		append_basic_variant_dict_entry(&dict, "Locations", DBUS_TYPE_UINT32, "u", &locations); | ||||
| 		append_basic_variant_dict_entry(&dict, "Context", DBUS_TYPE_UINT16, "q", &context); | ||||
| 		append_basic_variant_dict_entry(&dict, "SupportedContext", DBUS_TYPE_UINT16, "q", &supported_context); | ||||
| 		append_basic_variant_dict_entry(&dict, "Context", DBUS_TYPE_UINT16, "q", &contexts); | ||||
| 		append_basic_variant_dict_entry(&dict, "SupportedContext", DBUS_TYPE_UINT16, "q", &supported_contexts); | ||||
| 	} | ||||
| 
 | ||||
| 	dbus_message_iter_close_container(&entry, &dict); | ||||
|  | @ -5127,7 +5140,7 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage * | |||
| 				ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SINK, &endpoint); | ||||
| 				if (ret == 0) { | ||||
| 					spa_log_info(monitor->log, "register media sink codec %s: %s", media_codecs[i]->name, endpoint); | ||||
| 					append_media_object(&array, endpoint, | ||||
| 					append_media_object(monitor, &array, endpoint, | ||||
| 					        codec->bap ? SPA_BT_UUID_BAP_SINK : SPA_BT_UUID_A2DP_SINK, | ||||
| 							codec_id, caps, caps_size); | ||||
| 				} | ||||
|  | @ -5142,7 +5155,7 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage * | |||
| 				ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SOURCE, &endpoint); | ||||
| 				if (ret == 0) { | ||||
| 					spa_log_info(monitor->log, "register media source codec %s: %s", media_codecs[i]->name, endpoint); | ||||
| 					append_media_object(&array, endpoint, | ||||
| 					append_media_object(monitor, &array, endpoint, | ||||
| 					        codec->bap ? SPA_BT_UUID_BAP_SOURCE : SPA_BT_UUID_A2DP_SOURCE, | ||||
| 							codec_id, caps, caps_size); | ||||
| 				} | ||||
|  | @ -5158,7 +5171,7 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage * | |||
| 					ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SOURCE_BROADCAST, &endpoint); | ||||
| 						if (ret == 0) { | ||||
| 							spa_log_info(monitor->log, "register media source codec %s: %s", media_codecs[i]->name, endpoint); | ||||
| 							append_media_object(&array, endpoint, | ||||
| 							append_media_object(monitor, &array, endpoint, | ||||
| 									SPA_BT_UUID_BAP_BROADCAST_SOURCE, | ||||
| 									codec_id, caps, caps_size); | ||||
| 						} | ||||
|  | @ -5173,7 +5186,7 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage * | |||
| 					ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SINK_BROADCAST, &endpoint); | ||||
| 					if (ret == 0) { | ||||
| 						spa_log_info(monitor->log, "register broadcast media sink codec %s: %s", media_codecs[i]->name, endpoint); | ||||
| 						append_media_object(&array, endpoint, | ||||
| 						append_media_object(monitor, &array, endpoint, | ||||
| 								SPA_BT_UUID_BAP_BROADCAST_SINK, | ||||
| 								codec_id, caps, caps_size); | ||||
| 					} | ||||
|  | @ -6549,6 +6562,55 @@ fallback: | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_dict *info, | ||||
| 		const char *key, uint32_t *value) | ||||
| { | ||||
| 	const char *str; | ||||
| 	uint32_t position[SPA_AUDIO_MAX_CHANNELS]; | ||||
| 	uint32_t n_channels; | ||||
| 	uint32_t locations; | ||||
| 	unsigned int i, j; | ||||
| 
 | ||||
| 	if (!info || !(str = spa_dict_lookup(info, key))) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (spa_atou32(str, value, 0)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!spa_audio_parse_position(str, strlen(str), position, &n_channels)) { | ||||
| 		spa_log_error(this->log, "property %s '%s' is not valid position array", key, str); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	locations = 0; | ||||
| 	for (i = 0; i < n_channels; ++i) | ||||
| 		for (j = 0; j < SPA_N_ELEMENTS(bap_channel_bits); ++j) | ||||
| 			if (bap_channel_bits[j].channel == position[i]) | ||||
| 				locations |= bap_channel_bits[j].bit; | ||||
| 
 | ||||
| 	*value = locations; | ||||
| } | ||||
| 
 | ||||
| static void parse_bap_server(struct spa_bt_monitor *this, const struct spa_dict *info) | ||||
| { | ||||
| 	this->bap_sink_locations = BAP_CHANNEL_ALL; | ||||
| 	this->bap_source_locations = BAP_CHANNEL_ALL; | ||||
| 	this->bap_sink_contexts = this->bap_sink_supported_contexts = BAP_CONTEXT_ALL; | ||||
| 	this->bap_source_contexts = this->bap_source_supported_contexts = (BAP_CONTEXT_UNSPECIFIED | BAP_CONTEXT_CONVERSATIONAL | | ||||
| 					BAP_CONTEXT_MEDIA | BAP_CONTEXT_GAME); | ||||
| 
 | ||||
| 	if (!info) | ||||
| 		return; | ||||
| 
 | ||||
| 	parse_bap_locations(this, info, "bluez5.bap-server-capabilities.sink.locations", &this->bap_sink_locations); | ||||
| 	spa_atou32(spa_dict_lookup(info, "bluez5.bap-server-capabilities.sink.contexts"), &this->bap_sink_contexts, 0); | ||||
| 	spa_atou32(spa_dict_lookup(info, "bluez5.bap-server-capabilities.sink.supported-contexts"), &this->bap_sink_supported_contexts, 0); | ||||
| 
 | ||||
| 	parse_bap_locations(this, info, "bluez5.bap-server-capabilities.source.locations", &this->bap_source_locations); | ||||
| 	spa_atou32(spa_dict_lookup(info, "bluez5.bap-server-capabilities.source.contexts"), &this->bap_source_contexts, 0); | ||||
| 	spa_atou32(spa_dict_lookup(info, "bluez5.bap-server-capabilities.source.supported-contexts"), &this->bap_source_supported_contexts, 0); | ||||
| } | ||||
| 
 | ||||
| static void get_global_settings(struct spa_bt_monitor *this, const struct spa_dict *dict) | ||||
| { | ||||
| 	uint32_t n_items = 0; | ||||
|  | @ -6662,6 +6724,7 @@ impl_init(const struct spa_handle_factory *factory, | |||
| 
 | ||||
| 	parse_roles(this, info); | ||||
| 	parse_broadcast_source_config(this, info); | ||||
| 	parse_bap_server(this, info); | ||||
| 
 | ||||
| 	this->default_audio_info.rate = A2DP_CODEC_DEFAULT_RATE; | ||||
| 	this->default_audio_info.channels = A2DP_CODEC_DEFAULT_CHANNELS; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pauli Virtanen
						Pauli Virtanen