Message ID | 20250121144404.4087658-3-michal.dzik@streamunlimited.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | connect VCP profile to MediaTransport volume | expand |
Context | Check | Description |
---|---|---|
tedd_an/pre-ci_am | success | Success |
Hi Michal, On Tue, Jan 21, 2025 at 9:44 AM Michal Dzik <michal.dzik@streamunlimited.com> wrote: > > It is now possible to control absolute volume of remote volume renderer > device via dbus and also get notifications if the volume changes. > --- > Makefile.plugins | 2 +- > profiles/audio/transport.c | 37 +++++++++++++++++++------ > profiles/audio/vcp.c | 57 ++++++++++++++++++++++++++++++++++++-- > profiles/audio/vcp.h | 12 ++++++++ > 4 files changed, 95 insertions(+), 13 deletions(-) > create mode 100644 profiles/audio/vcp.h > > diff --git a/Makefile.plugins b/Makefile.plugins > index 97335d643..81cf3155a 100644 > --- a/Makefile.plugins > +++ b/Makefile.plugins > @@ -128,7 +128,7 @@ endif > > if VCP > builtin_modules += vcp > -builtin_sources += profiles/audio/vcp.c > +builtin_sources += profiles/audio/vcp.h profiles/audio/vcp.c > endif > > if MICP > diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c > index a4198d23a..eff95a7c2 100644 > --- a/profiles/audio/transport.c > +++ b/profiles/audio/transport.c > @@ -55,6 +55,7 @@ > #include "media.h" > #include "transport.h" > #include "bass.h" > +#include "vcp.h" > > #define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1" > > @@ -1420,6 +1421,7 @@ static const GDBusPropertyTable transport_bap_uc_properties[] = { > { "Location", "u", get_location }, > { "Metadata", "ay", get_metadata }, > { "Links", "ao", get_links, NULL, links_exists }, > + { "Volume", "q", get_volume, set_volume, volume_exists }, > { } > }; > > @@ -2188,6 +2190,17 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd, > bap_update_links(transport); > } > > +static int8_t transport_bap_get_volume(struct media_transport *transport) > +{ > + return bt_audio_vcp_get_volume(transport->device); > +} > + > +static int transport_bap_set_volume(struct media_transport *transport, > + int8_t volume) > +{ > + return bt_audio_vcp_set_volume(transport->device, volume) ? 0 : -EIO; > +} > + > static void transport_bap_destroy(void *data) > { > struct bap_transport *bap = data; > @@ -2411,7 +2424,8 @@ static void *transport_asha_init(struct media_transport *transport, void *data) > transport_bap_init, \ > transport_bap_resume, transport_bap_suspend, \ > transport_bap_cancel, _set_state, \ > - transport_bap_get_stream, NULL, NULL, NULL, \ > + transport_bap_get_stream, transport_bap_get_volume, \ > + transport_bap_set_volume, NULL, \ > _update_links, transport_bap_destroy) > > #define BAP_UC_OPS(_uuid) \ > @@ -2573,17 +2587,18 @@ struct btd_device *media_transport_get_dev(struct media_transport *transport) > void media_transport_update_volume(struct media_transport *transport, > int8_t volume) > { > - struct a2dp_transport *a2dp = transport->data; > - > if (volume < 0) > return; > > - /* Check if volume really changed */ > - if (a2dp->volume == volume) > - return; > + if (media_endpoint_get_sep(transport->endpoint)) { > + struct a2dp_transport *a2dp = transport->data; > > - a2dp->volume = volume; > + /* Check if volume really changed */ > + if (a2dp->volume == volume) > + return; > > + a2dp->volume = volume; > + } > g_dbus_emit_property_changed(btd_get_dbus_connection(), > transport->path, > MEDIA_TRANSPORT_INTERFACE, "Volume"); > @@ -2628,11 +2643,15 @@ void media_transport_update_device_volume(struct btd_device *dev, > /* Attempt to locate the transport to set its volume */ > for (l = transports; l; l = l->next) { > struct media_transport *transport = l->data; > + const char *uuid = media_endpoint_get_uuid(transport->endpoint); > if (transport->device != dev) > continue; > > - /* Volume is A2DP only */ > - if (media_endpoint_get_sep(transport->endpoint)) { > + /* Volume is A2DP and BAP only */ > + if (media_endpoint_get_sep(transport->endpoint) || > + strcasecmp(uuid, PAC_SINK_UUID) || > + strcasecmp(uuid, PAC_SOURCE_UUID) || > + strcasecmp(uuid, BAA_SERVICE_UUID)) { > media_transport_update_volume(transport, volume); > break; > } > diff --git a/profiles/audio/vcp.c b/profiles/audio/vcp.c > index 175275f2e..cc6f352c1 100644 > --- a/profiles/audio/vcp.c > +++ b/profiles/audio/vcp.c > @@ -51,6 +51,9 @@ > #include "src/log.h" > #include "src/error.h" > > +#include "vcp.h" > +#include "transport.h" > + > #define VCS_UUID_STR "00001844-0000-1000-8000-00805f9b34fb" > #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" > > @@ -83,6 +86,33 @@ static struct vcp_data *vcp_data_new(struct btd_device *device) > return data; > } > > +static bool match_data(const void *data, const void *match_data) > +{ > + const struct vcp_data *vdata = data; > + const struct bt_vcp *vcp = match_data; > + > + return vdata->vcp == vcp; > +} > + > +static int8_t scale_volume_vcp2transport(uint8_t volume) > +{ > + /* Transport has volume range 0-127, VCP has range 0-255 */ > + return volume / 2; > +} > + > +static uint8_t scale_volume_transport2vcp(int8_t volume) > +{ > + return volume * 2; > +} Now I understand why you are doing this, it seems there is an assumption that we should stick to 0-127 range from A2DP, but perhaps we document that Transport.Volume range is dependent on the Transport.UUID so for VCP we can use 0-255, in case you were looking into how ASHA was implemented that has a different problem because Volume is returning uint16 we can't really use negative numbers thus why that uses scaling. Anyway for now let go this way but if you have time please just update the document so we can remove this scaling and leave it for the clients to interpret based on the Transport.UUID. > +static void vcp_volume_changed(struct bt_vcp *vcp, uint8_t volume) > +{ > + struct vcp_data *data = queue_find(sessions, match_data, vcp); > + > + if (data) > + media_transport_update_device_volume(data->device, scale_volume_vcp2transport(volume)); > +} > + > static void vcp_data_add(struct vcp_data *data) > { > DBG("data %p", data); > @@ -93,6 +123,7 @@ static void vcp_data_add(struct vcp_data *data) > } > > bt_vcp_set_debug(data->vcp, vcp_debug, NULL, NULL); > + bt_vcp_set_volume_callback(data->vcp, vcp_volume_changed); > > if (!sessions) > sessions = queue_new(); > @@ -103,12 +134,12 @@ static void vcp_data_add(struct vcp_data *data) > btd_service_set_user_data(data->service, data); > } > > -static bool match_data(const void *data, const void *match_data) > +static bool match_device(const void *data, const void *match_data) > { > const struct vcp_data *vdata = data; > - const struct bt_vcp *vcp = match_data; > + const struct btd_device *device = match_data; > > - return vdata->vcp == vcp; > + return vdata->device == device; > } > > static void vcp_data_free(struct vcp_data *data) > @@ -137,6 +168,26 @@ static void vcp_data_remove(struct vcp_data *data) > } > } > > +int8_t bt_audio_vcp_get_volume(struct btd_device *device) > +{ > + struct vcp_data *data = queue_find(sessions, match_device, device); > + > + if (data) > + return scale_volume_vcp2transport(bt_vcp_get_volume(data->vcp)); > + > + return 0; > +} > + > +bool bt_audio_vcp_set_volume(struct btd_device *device, int8_t volume) > +{ > + struct vcp_data *data = queue_find(sessions, match_device, device); > + > + if (data) > + return bt_vcp_set_volume(data->vcp, scale_volume_transport2vcp(volume)); > + > + return FALSE; > +} > + > static void vcp_detached(struct bt_vcp *vcp, void *user_data) > { > struct vcp_data *data; > diff --git a/profiles/audio/vcp.h b/profiles/audio/vcp.h > new file mode 100644 > index 000000000..f313cd96a > --- /dev/null > +++ b/profiles/audio/vcp.h > @@ -0,0 +1,12 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2024 StreamUnlimited Engineering GmbH > + * > + * > + */ > + > +int8_t bt_audio_vcp_get_volume(struct btd_device *device); > +bool bt_audio_vcp_set_volume(struct btd_device *device, int8_t volume); > -- > 2.34.1 > >
diff --git a/Makefile.plugins b/Makefile.plugins index 97335d643..81cf3155a 100644 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -128,7 +128,7 @@ endif if VCP builtin_modules += vcp -builtin_sources += profiles/audio/vcp.c +builtin_sources += profiles/audio/vcp.h profiles/audio/vcp.c endif if MICP diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c index a4198d23a..eff95a7c2 100644 --- a/profiles/audio/transport.c +++ b/profiles/audio/transport.c @@ -55,6 +55,7 @@ #include "media.h" #include "transport.h" #include "bass.h" +#include "vcp.h" #define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1" @@ -1420,6 +1421,7 @@ static const GDBusPropertyTable transport_bap_uc_properties[] = { { "Location", "u", get_location }, { "Metadata", "ay", get_metadata }, { "Links", "ao", get_links, NULL, links_exists }, + { "Volume", "q", get_volume, set_volume, volume_exists }, { } }; @@ -2188,6 +2190,17 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd, bap_update_links(transport); } +static int8_t transport_bap_get_volume(struct media_transport *transport) +{ + return bt_audio_vcp_get_volume(transport->device); +} + +static int transport_bap_set_volume(struct media_transport *transport, + int8_t volume) +{ + return bt_audio_vcp_set_volume(transport->device, volume) ? 0 : -EIO; +} + static void transport_bap_destroy(void *data) { struct bap_transport *bap = data; @@ -2411,7 +2424,8 @@ static void *transport_asha_init(struct media_transport *transport, void *data) transport_bap_init, \ transport_bap_resume, transport_bap_suspend, \ transport_bap_cancel, _set_state, \ - transport_bap_get_stream, NULL, NULL, NULL, \ + transport_bap_get_stream, transport_bap_get_volume, \ + transport_bap_set_volume, NULL, \ _update_links, transport_bap_destroy) #define BAP_UC_OPS(_uuid) \ @@ -2573,17 +2587,18 @@ struct btd_device *media_transport_get_dev(struct media_transport *transport) void media_transport_update_volume(struct media_transport *transport, int8_t volume) { - struct a2dp_transport *a2dp = transport->data; - if (volume < 0) return; - /* Check if volume really changed */ - if (a2dp->volume == volume) - return; + if (media_endpoint_get_sep(transport->endpoint)) { + struct a2dp_transport *a2dp = transport->data; - a2dp->volume = volume; + /* Check if volume really changed */ + if (a2dp->volume == volume) + return; + a2dp->volume = volume; + } g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Volume"); @@ -2628,11 +2643,15 @@ void media_transport_update_device_volume(struct btd_device *dev, /* Attempt to locate the transport to set its volume */ for (l = transports; l; l = l->next) { struct media_transport *transport = l->data; + const char *uuid = media_endpoint_get_uuid(transport->endpoint); if (transport->device != dev) continue; - /* Volume is A2DP only */ - if (media_endpoint_get_sep(transport->endpoint)) { + /* Volume is A2DP and BAP only */ + if (media_endpoint_get_sep(transport->endpoint) || + strcasecmp(uuid, PAC_SINK_UUID) || + strcasecmp(uuid, PAC_SOURCE_UUID) || + strcasecmp(uuid, BAA_SERVICE_UUID)) { media_transport_update_volume(transport, volume); break; } diff --git a/profiles/audio/vcp.c b/profiles/audio/vcp.c index 175275f2e..cc6f352c1 100644 --- a/profiles/audio/vcp.c +++ b/profiles/audio/vcp.c @@ -51,6 +51,9 @@ #include "src/log.h" #include "src/error.h" +#include "vcp.h" +#include "transport.h" + #define VCS_UUID_STR "00001844-0000-1000-8000-00805f9b34fb" #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" @@ -83,6 +86,33 @@ static struct vcp_data *vcp_data_new(struct btd_device *device) return data; } +static bool match_data(const void *data, const void *match_data) +{ + const struct vcp_data *vdata = data; + const struct bt_vcp *vcp = match_data; + + return vdata->vcp == vcp; +} + +static int8_t scale_volume_vcp2transport(uint8_t volume) +{ + /* Transport has volume range 0-127, VCP has range 0-255 */ + return volume / 2; +} + +static uint8_t scale_volume_transport2vcp(int8_t volume) +{ + return volume * 2; +} + +static void vcp_volume_changed(struct bt_vcp *vcp, uint8_t volume) +{ + struct vcp_data *data = queue_find(sessions, match_data, vcp); + + if (data) + media_transport_update_device_volume(data->device, scale_volume_vcp2transport(volume)); +} + static void vcp_data_add(struct vcp_data *data) { DBG("data %p", data); @@ -93,6 +123,7 @@ static void vcp_data_add(struct vcp_data *data) } bt_vcp_set_debug(data->vcp, vcp_debug, NULL, NULL); + bt_vcp_set_volume_callback(data->vcp, vcp_volume_changed); if (!sessions) sessions = queue_new(); @@ -103,12 +134,12 @@ static void vcp_data_add(struct vcp_data *data) btd_service_set_user_data(data->service, data); } -static bool match_data(const void *data, const void *match_data) +static bool match_device(const void *data, const void *match_data) { const struct vcp_data *vdata = data; - const struct bt_vcp *vcp = match_data; + const struct btd_device *device = match_data; - return vdata->vcp == vcp; + return vdata->device == device; } static void vcp_data_free(struct vcp_data *data) @@ -137,6 +168,26 @@ static void vcp_data_remove(struct vcp_data *data) } } +int8_t bt_audio_vcp_get_volume(struct btd_device *device) +{ + struct vcp_data *data = queue_find(sessions, match_device, device); + + if (data) + return scale_volume_vcp2transport(bt_vcp_get_volume(data->vcp)); + + return 0; +} + +bool bt_audio_vcp_set_volume(struct btd_device *device, int8_t volume) +{ + struct vcp_data *data = queue_find(sessions, match_device, device); + + if (data) + return bt_vcp_set_volume(data->vcp, scale_volume_transport2vcp(volume)); + + return FALSE; +} + static void vcp_detached(struct bt_vcp *vcp, void *user_data) { struct vcp_data *data; diff --git a/profiles/audio/vcp.h b/profiles/audio/vcp.h new file mode 100644 index 000000000..f313cd96a --- /dev/null +++ b/profiles/audio/vcp.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2024 StreamUnlimited Engineering GmbH + * + * + */ + +int8_t bt_audio_vcp_get_volume(struct btd_device *device); +bool bt_audio_vcp_set_volume(struct btd_device *device, int8_t volume);