stream: Make capability device IDs an JSON object

Also change the encoding of the byte array to use hexadecimal encoding,
i.e. the byte array [100, 200] in becomes "64c8".
This commit is contained in:
Jonas Ådahl 2026-01-22 16:55:34 +01:00 committed by Wim Taymans
parent 8600721de0
commit f2c4452e8d
5 changed files with 110 additions and 80 deletions

View File

@ -365,10 +365,13 @@ with. This can be used to reduce the amount of devices that are queried for form
metadata, which can be a time consuming task, if devices needs to be woken up.
To achieve this, the consumer adds another \ref SPA_PARAM_PeerCapability item with the key
\ref PW_CAPABILITY_DEVICE_IDS set to a string of base 64 encoded `dev_t` device IDs.
\ref PW_CAPABILITY_DEVICE_IDS set to a JSON object describing what device IDs are supported.
This JSON object as of version 1 contains a single key "available-devices" that contain
a list of hexadecimal encoded `dev_t` device IDs.
```
char *device_ids = ...; /* Base 64 encoding of a dev_t. */.
char *device_ids = "{\"available-devices\": [\"6464000000000000\",\"c8c8000000000000\"]}";
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1"),
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)));

View File

@ -1,46 +0,0 @@
/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
/* SPDX-License-Identifier: MIT */
static inline void base64_encode(const uint8_t *data, size_t len, char *enc, char pad)
{
static const char tab[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t i;
for (i = 0; i < len; i += 3) {
uint32_t v;
v = data[i+0] << 16;
v |= (i+1 < len ? data[i+1] : 0) << 8;
v |= (i+2 < len ? data[i+2] : 0);
*enc++ = tab[(v >> (3*6)) & 0x3f];
*enc++ = tab[(v >> (2*6)) & 0x3f];
*enc++ = i+1 < len ? tab[(v >> (1*6)) & 0x3f] : pad;
*enc++ = i+2 < len ? tab[(v >> (0*6)) & 0x3f] : pad;
}
*enc = '\0';
}
static inline size_t base64_decode(const char *data, size_t len, uint8_t *dec)
{
uint8_t tab[] = {
62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
-1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
-1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
size_t i, j;
for (i = 0, j = 0; i < len; i += 4) {
uint32_t v;
v = tab[data[i+0]-43] << (3*6);
v |= tab[data[i+1]-43] << (2*6);
v |= (data[i+2] == '=' ? 0 : tab[data[i+2]-43]) << (1*6);
v |= (data[i+3] == '=' ? 0 : tab[data[i+3]-43]);
dec[j++] = (v >> 16) & 0xff;
if (data[i+2] != '=') dec[j++] = (v >> 8) & 0xff;
if (data[i+3] != '=') dec[j++] = v & 0xff;
}
return j;
}

60
src/examples/utils.h Normal file
View File

@ -0,0 +1,60 @@
/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2026 Red Hat */
/* SPDX-License-Identifier: MIT */
static inline char *
encode_hex(const uint8_t *data, size_t size)
{
FILE *ms;
char *encoded = NULL;
size_t encoded_size = 0;
size_t i;
ms = open_memstream(&encoded, &encoded_size);
for (i = 0; i < size; i++) {
fprintf(ms, "%02x", data[i]);
}
fclose(ms);
return encoded;
}
static inline int8_t
ascii_hex_to_hex(uint8_t ascii_hex)
{
if (ascii_hex >= '0' && ascii_hex <= '9')
return ascii_hex - '0';
else if (ascii_hex >= 'a' && ascii_hex <= 'f')
return ascii_hex - 'a' + 10;
else if (ascii_hex >= 'A' && ascii_hex <= 'F')
return ascii_hex - 'A' + 10;
else
return -1;
}
static inline int
decode_hex(const char *encoded, uint8_t *data, size_t size)
{
size_t length;
size_t i;
length = strlen(encoded);
if (size < (length / 2) * sizeof(uint8_t))
return -1;
i = 0;
while (i < length) {
int8_t top = ascii_hex_to_hex(encoded[i]);
int8_t bottom = ascii_hex_to_hex(encoded[i + 1]);
if (top == -1 || bottom == -1)
return -1;
uint8_t el = top << 4 | bottom;
data[i / 2] = el;
i += 2;
}
return 1;
}

View File

@ -29,7 +29,7 @@
#include <pipewire/pipewire.h>
#include <pipewire/capabilities.h>
#include "base64.h"
#include "utils.h"
/* Comment out to test device ID negotation backward compatibility. */
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
@ -372,46 +372,56 @@ collect_device_ids(struct data *data, const char *json)
int len;
const char *value;
struct spa_json sub;
char key[1024];
if ((len = spa_json_begin(&it, json, strlen(json), &value)) <= 0) {
fprintf(stderr, "invalid device IDs value\n");
return;
}
if (!spa_json_is_array(value, len)) {
fprintf(stderr, "device IDs not array\n");
if (!spa_json_is_object(value, len)) {
fprintf(stderr, "device IDs not object\n");
return;
}
spa_json_enter(&it, &sub);
while ((len = spa_json_next(&sub, &value)) > 0) {
char *string;
union {
dev_t device_id;
uint8_t buffer[1024];
} dec;
while ((len = spa_json_object_next(&sub, key, sizeof(key), &value)) > 0) {
struct spa_json devices_sub;
string = alloca(len + 1);
if (!spa_json_is_string(value, len)) {
fprintf(stderr, "device ID not string\n");
if (!spa_json_is_array(value, len)) {
fprintf(stderr, "available-devices not array\n");
return;
}
if (spa_json_parse_string(value, len, string) <= 0) {
fprintf(stderr, "invalid device ID string\n");
return;
spa_json_enter(&sub, &devices_sub);
while ((len = spa_json_next(&devices_sub, &value)) > 0) {
char *string;
union {
dev_t device_id;
uint8_t buffer[1024];
} dec;
string = alloca(len + 1);
if (!spa_json_is_string(value, len)) {
fprintf(stderr, "device ID not string\n");
return;
}
if (spa_json_parse_string(value, len, string) <= 0) {
fprintf(stderr, "invalid device ID string\n");
return;
}
if (decode_hex(string, dec.buffer, sizeof (dec.buffer)) < 0) {
fprintf(stderr, "invalid device ID string\n");
return;
}
fprintf(stderr, "discovered device ID %u:%u\n",
major(dec.device_id), minor(dec.device_id));
data->device_ids[data->n_device_ids++] = dec.device_id;
}
if (base64_decode(string, strlen(string),
(uint8_t *)&dec.device_id) < sizeof(dev_t)) {
fprintf(stderr, "invalid device ID\n");
return;
}
fprintf(stderr, "discovered device ID %u:%u\n",
major(dec.device_id), minor(dec.device_id));
data->device_ids[data->n_device_ids++] = dec.device_id;
}
}

View File

@ -30,7 +30,7 @@
#include <pipewire/pipewire.h>
#include <pipewire/capabilities.h>
#include "base64.h"
#include "utils.h"
/* Comment out to test device ID negotation backward compatibility. */
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
@ -784,17 +784,20 @@ int main(int argc, char *argv[])
size_t i;
ms = open_memstream(&device_ids, &device_ids_size);
fprintf(ms, "[");
fprintf(ms, "{\"available-devices\": [");
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
dev_t device_id = makedev(devices[i].major, devices[i].minor);
char device_id_encoded[256];
char *device_id_encoded;
device_id_encoded = encode_hex((const uint8_t *) &device_id, sizeof (device_id));
base64_encode((const uint8_t *) &device_id, sizeof (device_id), device_id_encoded, '\0');
if (i > 0)
fprintf(ms, ",");
fprintf(ms, "\"%s\"", device_id_encoded);
free(device_id_encoded);
}
fprintf(ms, "]");
fprintf(ms, "]}");
fclose(ms);
#endif /* SUPPORT_DEVICE_IDS_LIST */