Skip to content

Commit

Permalink
Select A2DP codec configuration with CLI tool
Browse files Browse the repository at this point in the history
  • Loading branch information
arkq committed Jul 30, 2021
1 parent f6cb61f commit 104c323
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 122 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ PKG_CHECK_MODULES([ALSA], [alsa])
PKG_CHECK_MODULES([BLUEZ], [bluez >= 5.0])
PKG_CHECK_MODULES([DBUS1], [dbus-1 >= 1.6])
PKG_CHECK_MODULES([GIO2], [gio-unix-2.0])
PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.30])
PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.32])
PKG_CHECK_MODULES([SBC], [sbc >= 1.2])

PKG_CHECK_MODULES([LIBBSD], [libbsd >= 0.8],
Expand Down
9 changes: 7 additions & 2 deletions doc/bluealsa-cli.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ bluealsa-cli
a simple command line interface for the BlueALSA D-Bus API
----------------------------------------------------------

:Date: February 2021
:Date: July 2021
:Manual section: 1
:Manual group: General Commands Manual
:Version: $VERSION$
Expand Down Expand Up @@ -61,13 +61,18 @@ info *PCM_PATH*

The list of available codecs requires BlueZ SEP support (BlueZ >= 5.52)

codec *PCM_PATH* [*CODEC*]
codec *PCM_PATH* [*CODEC*] [*CONFIG*]
If *CODEC* is given, change the codec to be used by the given PCM. This
command will terminate the PCM if it is currently running.

If *CODEC* is not given, print a list of additional codecs supported by the
given PCM and the currently selected codec.

Optionally, for A2DP codecs, one can specify A2DP codec configuration which
should be selected. The *CONFIG* shall be given as a hexadecimal string. If
this parameter is omitted, BlueALSA will select default configuration based
on codec capabilities of connected Bluetooth device.

Selecting a codec and listing available codecs requires BlueZ SEP support
(BlueZ >= 5.52).

Expand Down
39 changes: 18 additions & 21 deletions src/bluealsa-dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,8 @@ static bool ba_variant_populate_sep(GVariantBuilder *props, const struct a2dp_se
}

g_variant_builder_init(props, G_VARIANT_TYPE("a{sv}"));

GVariantBuilder vcaps;
g_variant_builder_init(&vcaps, G_VARIANT_TYPE("ay"));

size_t i;
for (i = 0; i < size; i++)
g_variant_builder_add(&vcaps, "y", caps[i]);

g_variant_builder_add(props, "{sv}", "Capabilities", g_variant_builder_end(&vcaps));
g_variant_builder_add(props, "{sv}", "Capabilities", g_variant_new_fixed_array(
G_VARIANT_TYPE_BYTE, caps, size, sizeof(uint8_t)));

switch (codec->codec_id) {
case A2DP_CODEC_SBC:
Expand Down Expand Up @@ -556,13 +549,13 @@ static void bluealsa_pcm_select_codec(GDBusMethodInvocation *inv) {
GVariantIter *properties;
GVariant *value = NULL;
const char *errmsg = NULL;
const char *codec_name;
const char *property;
const char *codec;

void *a2dp_configuration = NULL;
a2dp_t a2dp_configuration = {};
size_t a2dp_configuration_size = 0;

g_variant_get(params, "(sa{sv})", &codec, &properties);
g_variant_get(params, "(sa{sv})", &codec_name, &properties);
while (g_variant_iter_next(properties, "{&sv}", &property, &value)) {

if (strcmp(property, "Configuration") == 0 &&
Expand All @@ -571,8 +564,13 @@ static void bluealsa_pcm_select_codec(GDBusMethodInvocation *inv) {
const void *data = g_variant_get_fixed_array(value,
&a2dp_configuration_size, sizeof(char));

g_free(a2dp_configuration);
a2dp_configuration = g_memdup(data, a2dp_configuration_size);
if (a2dp_configuration_size > sizeof(a2dp_configuration)) {
warn("Configuration blob size exceeded: %zu > %zu",
a2dp_configuration_size, sizeof(a2dp_configuration));
a2dp_configuration_size = sizeof(a2dp_configuration);
}

memcpy(&a2dp_configuration, data, a2dp_configuration_size);

}

Expand All @@ -588,7 +586,7 @@ static void bluealsa_pcm_select_codec(GDBusMethodInvocation *inv) {
goto fail;
}

uint16_t codec_id = ba_transport_codecs_a2dp_from_string(codec);
uint16_t codec_id = ba_transport_codecs_a2dp_from_string(codec_name);
enum a2dp_dir dir = !t->a2dp.codec->dir;
const GArray *seps = t->d->seps;
struct a2dp_sep *sep = NULL;
Expand Down Expand Up @@ -619,13 +617,13 @@ static void bluealsa_pcm_select_codec(GDBusMethodInvocation *inv) {
goto fail;

/* use codec configuration blob provided by user */
if (a2dp_configuration != NULL) {
if (a2dp_check_configuration(codec, a2dp_configuration,
if (a2dp_configuration_size != 0) {
if (a2dp_check_configuration(codec, &a2dp_configuration,
a2dp_configuration_size) != A2DP_CHECK_OK) {
errmsg = "Invalid configuration blob";
goto fail;
}
memcpy(sep->configuration, a2dp_configuration, sep->capabilities_size);
memcpy(sep->configuration, &a2dp_configuration, sep->capabilities_size);
}

if (ba_transport_select_codec_a2dp(t, sep) == -1)
Expand All @@ -634,7 +632,7 @@ static void bluealsa_pcm_select_codec(GDBusMethodInvocation *inv) {
}
else {

uint16_t codec_id = ba_transport_codecs_hfp_from_string(codec);
uint16_t codec_id = ba_transport_codecs_hfp_from_string(codec_name);
if (ba_transport_select_codec_sco(t, codec_id) == -1)
goto fail;

Expand All @@ -646,13 +644,12 @@ static void bluealsa_pcm_select_codec(GDBusMethodInvocation *inv) {
fail:
if (errmsg == NULL)
errmsg = strerror(errno);
error("Couldn't select codec: %s: %s", codec, errmsg);
error("Couldn't select codec: %s: %s", codec_name, errmsg);
g_dbus_method_invocation_return_error(inv, G_DBUS_ERROR,
G_DBUS_ERROR_FAILED, "%s", errmsg);

final:
ba_transport_pcm_unref(pcm);
g_free(a2dp_configuration);
g_variant_iter_free(properties);
if (value != NULL)
g_variant_unref(value);
Expand Down
32 changes: 7 additions & 25 deletions src/bluez.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,9 @@ static void bluez_endpoint_select_configuration(GDBusMethodInvocation *inv) {
if (a2dp_select_configuration(codec, &capabilities, size) == -1)
goto fail;

GVariantBuilder caps;
size_t i;

g_variant_builder_init(&caps, G_VARIANT_TYPE("ay"));
for (i = 0; i < size; i++)
g_variant_builder_add(&caps, "y", ((char *)&capabilities)[i]);

g_dbus_method_invocation_return_value(inv, g_variant_new("(ay)", &caps));
g_variant_builder_clear(&caps);
GVariant *rv[] = {
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, &capabilities, size, sizeof(uint8_t)) };
g_dbus_method_invocation_return_value(inv, g_variant_new_tuple(rv, 1));

return;

Expand Down Expand Up @@ -404,26 +398,20 @@ static int bluez_register_media_endpoint(
const struct a2dp_codec *codec = dbus_obj->codec;
GDBusMessage *msg = NULL, *rep = NULL;
int ret = 0;
size_t i;

debug("Registering media endpoint: %s", dbus_obj->path);

msg = g_dbus_message_new_method_call(BLUEZ_SERVICE, adapter->bluez_dbus_path,
BLUEZ_IFACE_MEDIA, "RegisterEndpoint");

GVariantBuilder caps;
GVariantBuilder properties;

g_variant_builder_init(&caps, G_VARIANT_TYPE("ay"));
g_variant_builder_init(&properties, G_VARIANT_TYPE("a{sv}"));

for (i = 0; i < codec->capabilities_size; i++)
g_variant_builder_add(&caps, "y", ((char *)&codec->capabilities)[i]);

g_variant_builder_add(&properties, "{sv}", "UUID", g_variant_new_string(uuid));
g_variant_builder_add(&properties, "{sv}", "DelayReporting", g_variant_new_boolean(TRUE));
g_variant_builder_add(&properties, "{sv}", "Codec", g_variant_new_byte(codec->codec_id));
g_variant_builder_add(&properties, "{sv}", "Capabilities", g_variant_builder_end(&caps));
g_variant_builder_add(&properties, "{sv}", "Capabilities", g_variant_new_fixed_array(
G_VARIANT_TYPE_BYTE, &codec->capabilities, codec->capabilities_size, sizeof(uint8_t)));

g_dbus_message_set_body(msg, g_variant_new("(oa{sv})", dbus_obj->path, &properties));
g_variant_builder_clear(&properties);
Expand Down Expand Up @@ -1273,16 +1261,10 @@ bool bluez_a2dp_set_configuration(
goto fail;
}

GVariantBuilder caps;
g_variant_builder_init(&caps, G_VARIANT_TYPE("ay"));

size_t i;
for (i = 0; i < sep->capabilities_size; i++)
g_variant_builder_add(&caps, "y", ((char *)sep->configuration)[i]);

GVariantBuilder props;
g_variant_builder_init(&props, G_VARIANT_TYPE("a{sv}"));
g_variant_builder_add(&props, "{sv}", "Capabilities", g_variant_builder_end(&caps));
g_variant_builder_add(&props, "{sv}", "Capabilities", g_variant_new_fixed_array(
G_VARIANT_TYPE_BYTE, sep->configuration, sep->capabilities_size, sizeof(uint8_t)));

msg = g_dbus_message_new_method_call(BLUEZ_SERVICE,
sep->bluez_dbus_path, BLUEZ_IFACE_MEDIA_ENDPOINT, "SetConfiguration");
Expand Down
59 changes: 59 additions & 0 deletions src/shared/hex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* BlueALSA - hex.c
* Copyright (c) 2016-2021 Arkadiusz Bokowy
*
* This file is a part of bluez-alsa.
*
* This project is licensed under the terms of the MIT license.
*
*/

#include "shared/hex.h"

#include <errno.h>
#include <stdio.h>

static const int hextable[255] = {
['0'] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
['A'] = 10, 11, 12, 13, 14, 15,
['a'] = 10, 11, 12, 13, 14, 15,
};

/**
* Encode a binary data into a hex string.
*
* @param bin A buffer with binary data.
* @param hex A buffer where null-terminated hexadecimal string will be
* stored. This buffer has to be big enough to store n*2 + 1 bytes of
* data.
* @param n The length of the binary buffer which shall be encoded.
* @return This function returns length of the hex string. */
ssize_t bin2hex(const void *bin, char *hex, size_t n) {
for (size_t i = 0; i < n; i++)
sprintf(&hex[i * 2], "%.2x", ((unsigned char *)bin)[i]);
return n * 2;
}

/**
* Decode a hex string into a binary data.
*
* @param hex A buffer with hexadecimal string.
* @param bin A buffer where decoded data will be stored. This buffer has to
* be big enough to store n/2 bytes of data.
* @param n The length of the string which shall be decoded.
* @return On success this function returns the size of the binary data. If
* an error has occurred, -1 is returned and errno is set to indicate the
* error. */
ssize_t hex2bin(const char *hex, void *bin, size_t n) {

if (n % 2 != 0)
return errno = EINVAL, -1;

n /= 2;
for (size_t i = 0; i < n; i++) {
((char *)bin)[i] = hextable[(int)hex[i * 2]] << 4;
((char *)bin)[i] |= hextable[(int)hex[i * 2 + 1]];
}

return n;
}
24 changes: 24 additions & 0 deletions src/shared/hex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* BlueALSA - hex.h
* Copyright (c) 2016-2021 Arkadiusz Bokowy
*
* This file is a part of bluez-alsa.
*
* This project is licensed under the terms of the MIT license.
*
*/

#pragma once
#ifndef BLUEALSA_SHARED_HEX_H_
#define BLUEALSA_SHARED_HEX_H_

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

ssize_t bin2hex(const void *bin, char *hex, size_t n);
ssize_t hex2bin(const char *hex, void *bin, size_t n);

#endif
3 changes: 3 additions & 0 deletions utils/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ bin_PROGRAMS =

if ENABLE_A2DPCONF
bin_PROGRAMS += a2dpconf
a2dpconf_SOURCES = \
../src/shared/hex.c \
a2dpconf.c
a2dpconf_CFLAGS = \
-I$(top_srcdir)/src
a2dpconf_LDADD = \
Expand Down
Loading

0 comments on commit 104c323

Please sign in to comment.