Skip to content

Commit

Permalink
client/player: Add support for broadcast source
Browse files Browse the repository at this point in the history
This adds bluetoothctl support for broadcast source.
To test the current implementation use bluetoothctl with the commands:
endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
endpoint.config <created endpoint> <local endpoint> 16_2_1
transport.acquire <created transport>
transport.send <created transport> <file.wav>
  • Loading branch information
silviubarbulescu authored and Vudentz committed Jun 7, 2023
1 parent eeb349b commit eb82174
Showing 1 changed file with 201 additions and 25 deletions.
226 changes: 201 additions & 25 deletions client/player.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@ struct endpoint {
bool auto_accept;
bool acquiring;
uint8_t max_transports;
uint8_t cig;
uint8_t cis;
uint8_t iso_group;
uint8_t iso_stream;
char *transport;
DBusMessage *msg;
struct preset *preset;
bool broadcast;
struct iovec *bcode;
};

static DBusConnection *dbus_conn;
Expand All @@ -104,6 +106,22 @@ struct transport {
struct io *timer_io;
};

static const uint8_t base_lc3_16_2_1[] = {
0x28, 0x00, 0x00, /* Presentation Delay */
0x01, /* Number of Subgroups */
0x01, /* Number of BIS */
0x06, 0x00, 0x00, 0x00, 0x00, /* Code ID = LC3 (0x06) */
0x11, /* Codec Specific Configuration */
0x02, 0x01, 0x03, /* 16 KHZ */
0x02, 0x02, 0x01, /* 10 ms */
0x05, 0x03, 0x01, 0x00, 0x00, 0x00, /* Front Left */
0x03, 0x04, 0x28, 0x00, /* Frame Length 40 bytes */
0x04, /* Metadata */
0x03, 0x02, 0x02, 0x00, /* Audio Context: Convertional */
0x01, /* BIS */
0x00, /* Codec Specific Configuration */
};

static void endpoint_unregister(void *data)
{
struct endpoint *ep = data;
Expand Down Expand Up @@ -1154,6 +1172,16 @@ static const struct capabilities {
CODEC_CAPABILITIES(PAC_SOURCE_UUID, LC3_ID,
LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
3u, 30, 240)),
/* Broadcast LC3 Source:
*
* Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz
* Duration: 7.5 ms 10 ms
* Channel count: 3
* Frame length: 30-240
*/
CODEC_CAPABILITIES(BAA_SERVICE_UUID, LC3_ID,
LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
3u, 30, 240)),
};

struct codec_qos {
Expand Down Expand Up @@ -1435,6 +1463,7 @@ static struct preset {
PRESET(A2DP_SINK_UUID, A2DP_CODEC_SBC, sbc_presets, 6),
PRESET(PAC_SINK_UUID, LC3_ID, lc3_presets, 3),
PRESET(PAC_SOURCE_UUID, LC3_ID, lc3_presets, 3),
PRESET(BAA_SERVICE_UUID, LC3_ID, lc3_presets, 3),
};

static void parse_vendor_codec(const char *codec, uint16_t *vid, uint16_t *cid)
Expand Down Expand Up @@ -1707,13 +1736,35 @@ struct endpoint_config {
const struct codec_qos *qos;
};

#define BCODE {0x01, 0x02, 0x68, 0x05, 0x53, 0xf1, 0x41, 0x5a, \
0xa2, 0x65, 0xbb, 0xaf, 0xc6, 0xea, 0x03, 0xb8}

static struct bt_iso_qos bcast_qos = {
.bcast = {
.big = BT_ISO_QOS_BIG_UNSET,
.bis = BT_ISO_QOS_BIS_UNSET,
.sync_interval = 0x07,
.packing = 0x00,
.framing = 0x00,
.encryption = 0x00,
.bcode = BCODE,
.options = 0x00,
.skip = 0x0000,
.sync_timeout = 0x4000,
.sync_cte_type = 0x00,
.mse = 0x00,
.timeout = 0x4000,
}
};

static void append_properties(DBusMessageIter *iter,
struct endpoint_config *cfg)
{
DBusMessageIter dict;
struct codec_qos *qos = (void *)cfg->qos;
const char *key = "Capabilities";
const char *meta = "Metadata";
const char *keyBCode = "BroadcastCode";

dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);

Expand Down Expand Up @@ -1742,27 +1793,47 @@ static void append_properties(DBusMessageIter *iter,
DBUS_TYPE_BYTE, &cfg->target_latency);
}

if (cfg->ep->cig != BT_ISO_QOS_CIG_UNSET) {
bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->cig);
if ((!cfg->ep->broadcast) &&
(cfg->ep->iso_group != BT_ISO_QOS_GROUP_UNSET)) {
bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->iso_group);
g_dbus_dict_append_entry(&dict, "CIG", DBUS_TYPE_BYTE,
&cfg->ep->cig);
&cfg->ep->iso_group);
} else {
bt_shell_printf("BIG 0x%2.2x\n", bcast_qos.bcast.big);
g_dbus_dict_append_entry(&dict, "BIG", DBUS_TYPE_BYTE,
&bcast_qos.bcast.big);
}

if (cfg->ep->cis != BT_ISO_QOS_CIS_UNSET) {
bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->cis);
if ((!cfg->ep->broadcast) &&
(cfg->ep->iso_stream != BT_ISO_QOS_STREAM_UNSET)) {
bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->iso_stream);
g_dbus_dict_append_entry(&dict, "CIS", DBUS_TYPE_BYTE,
&cfg->ep->cis);
&cfg->ep->iso_stream);

} else {
bt_shell_printf("BIS 0x%2.2x\n", bcast_qos.bcast.bis);
g_dbus_dict_append_entry(&dict, "BIS", DBUS_TYPE_BYTE,
&bcast_qos.bcast.bis);
}

bt_shell_printf("Interval %u\n", qos->interval);

g_dbus_dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT32,
&qos->interval);

bt_shell_printf("Framing %s\n", qos->framing ? "true" : "false");
if (!cfg->ep->broadcast) {
bt_shell_printf("Framing %s\n",
qos->framing ? "true" : "false");

g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
&qos->framing);
g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
&qos->framing);
} else {
bt_shell_printf("Framing %s\n",
bcast_qos.bcast.framing ? "true" : "false");

g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
&bcast_qos.bcast.framing);
}

bt_shell_printf("PHY %s\n", qos->phy);

Expand All @@ -1787,6 +1858,57 @@ static void append_properties(DBusMessageIter *iter,
g_dbus_dict_append_entry(&dict, "Delay", DBUS_TYPE_UINT32,
&qos->delay);

if (!cfg->ep->broadcast)
goto done;

bt_shell_printf("SyncInterval %u\n", bcast_qos.bcast.sync_interval);

g_dbus_dict_append_entry(&dict, "SyncInterval", DBUS_TYPE_BYTE,
&bcast_qos.bcast.sync_interval);

bt_shell_printf("Encryption %u\n", bcast_qos.bcast.encryption);

g_dbus_dict_append_entry(&dict, "Encryption", DBUS_TYPE_BYTE,
&bcast_qos.bcast.encryption);

bt_shell_printf("Options %u\n", bcast_qos.bcast.options);

g_dbus_dict_append_entry(&dict, "Options", DBUS_TYPE_BYTE,
&bcast_qos.bcast.options);

bt_shell_printf("Skip %u\n", bcast_qos.bcast.skip);

g_dbus_dict_append_entry(&dict, "Skip", DBUS_TYPE_UINT16,
&bcast_qos.bcast.skip);

bt_shell_printf("SyncTimeout %u\n", bcast_qos.bcast.sync_timeout);

g_dbus_dict_append_entry(&dict, "SyncTimeout", DBUS_TYPE_UINT16,
&bcast_qos.bcast.sync_timeout);

bt_shell_printf("SyncCteType %u\n", bcast_qos.bcast.sync_cte_type);

g_dbus_dict_append_entry(&dict, "SyncCteType", DBUS_TYPE_BYTE,
&bcast_qos.bcast.sync_cte_type);

bt_shell_printf("MSE %u\n", bcast_qos.bcast.mse);

g_dbus_dict_append_entry(&dict, "MSE", DBUS_TYPE_BYTE,
&bcast_qos.bcast.mse);

bt_shell_printf("Timeout %u\n", bcast_qos.bcast.timeout);

g_dbus_dict_append_entry(&dict, "Timeout", DBUS_TYPE_UINT16,
&bcast_qos.bcast.timeout);

bt_shell_printf("BroadcastCode:\n");
bt_shell_hexdump(cfg->ep->bcode->iov_base, cfg->ep->bcode->iov_len);

g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &keyBCode,
DBUS_TYPE_BYTE,
&cfg->ep->bcode->iov_base,
cfg->ep->bcode->iov_len);

done:
dbus_message_iter_close_container(iter, &dict);
}
Expand Down Expand Up @@ -2239,14 +2361,14 @@ static void endpoint_register(struct endpoint *ep)

}

static void endpoint_cis(const char *input, void *user_data)
static void endpoint_iso_stream(const char *input, void *user_data)
{
struct endpoint *ep = user_data;
char *endptr = NULL;
int value;

if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
ep->cis = BT_ISO_QOS_CIS_UNSET;
ep->iso_stream = BT_ISO_QOS_STREAM_UNSET;
} else {
value = strtol(input, &endptr, 0);

Expand All @@ -2255,20 +2377,20 @@ static void endpoint_cis(const char *input, void *user_data)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

ep->cis = value;
ep->iso_stream = value;
}

endpoint_register(ep);
}

static void endpoint_cig(const char *input, void *user_data)
static void endpoint_iso_group(const char *input, void *user_data)
{
struct endpoint *ep = user_data;
char *endptr = NULL;
int value;

if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
ep->cig = BT_ISO_QOS_CIG_UNSET;
ep->iso_group = BT_ISO_QOS_GROUP_UNSET;
} else {
value = strtol(input, &endptr, 0);

Expand All @@ -2277,10 +2399,15 @@ static void endpoint_cig(const char *input, void *user_data)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

ep->cig = value;
ep->iso_group = value;
}

bt_shell_prompt_input(ep->path, "CIS (auto/value):", endpoint_cis, ep);
if (!ep->broadcast)
bt_shell_prompt_input(ep->path, "CIS (auto/value):",
endpoint_iso_stream, ep);
else
bt_shell_prompt_input(ep->path, "BIS (auto/value):",
endpoint_iso_stream, ep);
}

static void endpoint_max_transports(const char *input, void *user_data)
Expand All @@ -2302,13 +2429,24 @@ static void endpoint_max_transports(const char *input, void *user_data)
ep->max_transports = value;
}

bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_cig, ep);
if (ep->broadcast)
bt_shell_prompt_input(ep->path, "BIG (auto/value):",
endpoint_iso_group, ep);
else
bt_shell_prompt_input(ep->path, "CIG (auto/value):",
endpoint_iso_group, ep);
}

static void endpoint_auto_accept(const char *input, void *user_data)
{
struct endpoint *ep = user_data;

if (!strcmp(ep->uuid, BAA_SERVICE_UUID)) {
ep->broadcast = true;
} else {
ep->broadcast = false;
}

if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) {
ep->auto_accept = true;
bt_shell_prompt_input(ep->path, "Max Transports (auto/value):",
Expand All @@ -2321,7 +2459,12 @@ static void endpoint_auto_accept(const char *input, void *user_data)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_cig, ep);
if (ep->broadcast)
bt_shell_prompt_input(ep->path, "BIG (auto/value):",
endpoint_iso_group, ep);
else
bt_shell_prompt_input(ep->path, "CIG (auto/value):",
endpoint_iso_group, ep);
}

static void endpoint_set_metadata(const char *input, void *user_data)
Expand Down Expand Up @@ -2598,9 +2741,17 @@ static void cmd_config_endpoint(int argc, char *argv[])
goto fail;
}

/* Copy capabilities */
iov_append(&cfg->caps, preset->data.iov_base,
preset->data.iov_len);
if (cfg->ep->broadcast) {
iov_append(&cfg->ep->bcode, bcast_qos.bcast.bcode,
sizeof(bcast_qos.bcast.bcode));
/* Copy capabilities for broadcast*/
iov_append(&cfg->caps, base_lc3_16_2_1,
sizeof(base_lc3_16_2_1));
} else {
/* Copy capabilities */
iov_append(&cfg->caps, preset->data.iov_base,
preset->data.iov_len);
}

/* Set QoS parameters */
cfg->qos = &preset->qos;
Expand Down Expand Up @@ -3050,8 +3201,8 @@ static void register_endpoints(GDBusProxy *proxy)
ep->cid);
ep->max_transports = UINT8_MAX;
ep->auto_accept = true;
ep->cig = BT_ISO_QOS_CIG_UNSET;
ep->cis = BT_ISO_QOS_CIS_UNSET;
ep->iso_group = BT_ISO_QOS_GROUP_UNSET;
ep->iso_stream = BT_ISO_QOS_STREAM_UNSET;
endpoint_register(ep);
}
}
Expand Down Expand Up @@ -3595,6 +3746,7 @@ static void cmd_acquire_transport(int argc, char *argv[])
{
GDBusProxy *proxy;
int i;
struct endpoint *ep, *link;

for (i = 1; i < argc; i++) {
proxy = g_dbus_proxy_lookup(transports, NULL, argv[i],
Expand All @@ -3610,6 +3762,30 @@ static void cmd_acquire_transport(int argc, char *argv[])
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

ep = find_ep_by_transport(g_dbus_proxy_get_path(proxy));
if (!ep || ep->acquiring) {
bt_shell_printf(
"Transport %s already in acquiring process\n",
argv[i]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

ep->acquiring = true;

link = find_link_by_proxy(proxy);
if (link) {
bt_shell_printf("Link %s found\n", link->transport);
/* If link already acquiring wait it to be complete */
if (link->acquiring) {
bt_shell_printf(
"Link %s is in acquiring process\n",
argv[i]);
return bt_shell_noninteractive_quit(
EXIT_FAILURE);
}
link->acquiring = true;
}

if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
acquire_reply, proxy, NULL)) {
bt_shell_printf("Failed acquire transport\n");
Expand Down

0 comments on commit eb82174

Please sign in to comment.