examples: Make fixate examples support device ID negotation

Things are structured in a way allowing testing falling back to
implicitly assumed device by defining/not defining macros.
This commit is contained in:
Jonas Ådahl 2025-11-11 21:08:56 +01:00 committed by Wim Taymans
parent 5481a235ed
commit f055cf398d
3 changed files with 544 additions and 64 deletions

46
src/examples/base64.h Normal file
View file

@ -0,0 +1,46 @@
/* 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;
}

View file

@ -14,15 +14,25 @@
#include <signal.h>
#include <libdrm/drm_fourcc.h>
#include <sys/mman.h>
#include <sys/sysmacros.h>
#include <assert.h>
#include <spa/utils/json.h>
#include <spa/utils/result.h>
#include <spa/param/dict-utils.h>
#include <spa/param/peer-utils.h>
#include <spa/param/video/format-utils.h>
#include <spa/param/props.h>
#include <spa/debug/format.h>
#include <spa/debug/pod.h>
#include <pipewire/pipewire.h>
#include <pipewire/capabilities.h>
#include "base64.h"
/* Comment out to test device ID negotation backward compatibility. */
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
/* If defined, emulate failing to import DMA buffer. */
#define EMULATE_DMA_BUF_IMPORT_FAIL 1
@ -32,6 +42,8 @@
#define MAX_BUFFERS 64
#define MAX_MOD 8
#define MAX_PARAMS 16
#define MAX_DEVICE_IDS 16
#include "sdl.h"
@ -51,6 +63,35 @@ struct modifier_info {
uint64_t modifiers[MAX_MOD];
};
struct {
unsigned int major;
unsigned int minor;
struct modifier_info mod_info;
} devices[] = {
#if 1
{
.major = 100,
.minor = 100,
.mod_info = {
.spa_format = SPA_VIDEO_FORMAT_RGBA,
.modifiers = { DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID },
.n_modifiers = 2,
},
},
#endif
#if 1
{
.major = 200,
.minor = 200,
.mod_info = {
.spa_format = SPA_VIDEO_FORMAT_RGBA,
.modifiers = {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_GENERIC_16_16_TILE},
.n_modifiers = 2,
},
},
#endif
};
struct data {
const char *path;
@ -69,16 +110,20 @@ struct data {
int32_t stride;
struct spa_rectangle size;
uint32_t n_mod_info;
struct modifier_info mod_info[2];
int counter;
bool capabilities_known;
bool device_negotiation_supported;
dev_t device_ids[MAX_DEVICE_IDS];
size_t n_device_ids;
int used_device_index;
};
static int build_formats(struct data *data, struct spa_pod_builder *b, const struct spa_pod **params);
#ifdef EMULATE_DMA_BUF_IMPORT_FAIL
static struct pw_version parse_pw_version(const char* version) {
struct pw_version pw_version;
sscanf(version, "%d.%d.%d", &pw_version.major, &pw_version.minor,
@ -93,41 +138,38 @@ static bool has_pw_version(int major, int minor, int micro) {
return major <= pw_version.major && minor <= pw_version.minor && micro <= pw_version.micro;
}
static void init_modifiers(struct data *data)
{
data->n_mod_info = 1;
data->mod_info[0].spa_format = SPA_VIDEO_FORMAT_RGBA;
data->mod_info[0].n_modifiers = 2;
data->mod_info[0].modifiers[0] = DRM_FORMAT_MOD_LINEAR;
data->mod_info[0].modifiers[1] = DRM_FORMAT_MOD_INVALID;
}
static void destroy_modifiers(struct data *data)
{
data->mod_info[0].n_modifiers = 0;
}
static void strip_modifier(struct data *data, uint32_t spa_format, uint64_t modifier)
{
if (data->mod_info[0].spa_format != spa_format)
struct modifier_info *mod_info;
assert(data->used_device_index >= 0);
mod_info = &devices[data->used_device_index].mod_info;
if (mod_info->spa_format != spa_format)
return;
struct modifier_info *mod_info = &data->mod_info[0];
uint32_t counter = 0;
// Dropping of single modifiers is only supported on PipeWire 0.3.40 and newer.
// On older PipeWire just dropping all modifiers might work on Versions newer then 0.3.33/35
if (has_pw_version(0,3,40)) {
printf("Dropping a single modifier\n");
printf("Dropping a single modifier from device %u:%u\n",
devices[data->used_device_index].major,
devices[data->used_device_index].minor);
for (uint32_t i = 0; i < mod_info->n_modifiers; i++) {
if (mod_info->modifiers[i] == modifier)
continue;
mod_info->modifiers[counter++] = mod_info->modifiers[i];
}
} else {
printf("Dropping all modifiers\n");
printf("Dropping all modifiers from device %u:%u\n",
devices[data->used_device_index].major,
devices[data->used_device_index].minor);
counter = 0;
}
mod_info->n_modifiers = counter;
}
#endif /* EMULATE_DMA_BUF_IMPORT_FAIL */
static void handle_events(struct data *data)
{
@ -141,15 +183,36 @@ static void handle_events(struct data *data)
}
}
static struct spa_pod *build_format(struct spa_pod_builder *b, SDL_RendererInfo *info, enum spa_video_format format,
uint64_t *modifiers, int modifier_count)
static struct spa_pod *build_format(struct data *data, struct spa_pod_builder *b,
SDL_RendererInfo *info, enum spa_video_format format, int device_index)
{
struct spa_pod_frame f[2];
int i, c;
uint64_t *modifiers;
int modifier_count;
if (device_index == -1) {
modifiers = NULL;
modifier_count = 0;
} else {
struct modifier_info *mod_info = &devices[device_index].mod_info;
modifiers = mod_info->modifiers;
modifier_count = mod_info->n_modifiers;
}
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
/* device */
if (data->device_negotiation_supported && device_index >= 0) {
dev_t device_id = makedev(devices[device_index].major,
devices[device_index].minor);
spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_deviceId,
SPA_POD_PROP_FLAG_MANDATORY);
spa_pod_builder_bytes(b, &device_id, sizeof device_id);
}
/* format */
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
/* modifiers */
@ -303,25 +366,118 @@ static void on_stream_state_changed(void *_data, enum pw_stream_state old,
}
static void
on_stream_tag_changed(struct data *data, const struct spa_pod *param)
collect_device_ids(struct data *data, const char *json)
{
struct spa_json it;
int len;
const char *value;
struct spa_json sub;
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");
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;
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 (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;
}
}
static void
discover_capabilities(struct data *data, const struct spa_pod *param)
{
#ifdef SUPPORT_DEVICE_ID_NEGOTIATION
struct spa_peer_param_info info;
void *state = NULL;
while (spa_peer_param_parse(param, &info, sizeof(info), &state) == 1) {
struct spa_param_dict_info di;
if (spa_param_dict_parse(info.param, &di, sizeof(di)) > 0) {
struct spa_dict dict;
struct spa_dict_item *items;
const struct spa_dict_item *it;
if (spa_param_dict_info_parse(&di, sizeof(di), &dict, NULL) < 0)
return;
items = alloca(sizeof(struct spa_dict_item) * dict.n_items);
if (spa_param_dict_info_parse(&di, sizeof(di), &dict, items) < 0)
return;
spa_dict_for_each(it, &dict) {
if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION) &&
spa_streq(it->value, "true")) {
data->device_negotiation_supported = true;
} else if (spa_streq(it->key, PW_CAPABILITY_DEVICE_IDS)) {
collect_device_ids(data, it->value);
}
}
}
}
#endif /* SUPPORT_DEVICE_ID_NEGOTIATION */
}
static void
on_stream_peer_capability_changed(struct data *data, const struct spa_pod *param)
{
struct pw_stream *stream = data->stream;
printf("tag param changed: \n");
printf("peer capability param changed: \n");
spa_debug_pod(4, NULL, param);
discover_capabilities(data, param);
if (!data->capabilities_known) {
const struct spa_pod *params[2];
const struct spa_pod *params[MAX_PARAMS];
uint32_t n_params = 0;
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
data->capabilities_known = true;
if (data->device_negotiation_supported)
printf("stream supports device negotiation\n");
else
printf("stream does not support device negotiation\n");
/* build the extra parameters to connect with. To connect, we can provide
* a list of supported formats. We use a builder that writes the param
* object to the stack. */
printf("supported formats:\n");
n_params = build_formats(data, &b, params);
pw_stream_update_params(data->stream, params, n_params);
@ -330,6 +486,29 @@ on_stream_tag_changed(struct data *data, const struct spa_pod *param)
}
}
static int
find_device_id_from_param(const struct spa_pod *format, dev_t *device_id)
{
const struct spa_pod_prop *device_prop;
const void *bytes;
uint32_t size;
device_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_deviceId);
if (device_prop == NULL)
return -ENOENT;
if (spa_pod_parse_object (format, SPA_TYPE_OBJECT_Format, NULL,
SPA_FORMAT_VIDEO_deviceId, SPA_POD_OPT_Bytes(&bytes, &size)) < 0)
return -EINVAL;
if (size != sizeof(dev_t))
return -EINVAL;
*device_id = *(dev_t *)bytes;
return 0;
}
/* Be notified when the stream format param changes.
*
* We are now supposed to call pw_stream_finish_format() with success or
@ -346,7 +525,7 @@ on_stream_format_changed(struct data *data, const struct spa_pod *param)
struct pw_stream *stream = data->stream;
uint8_t params_buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
const struct spa_pod *params[1];
const struct spa_pod *params[MAX_PARAMS];
uint32_t n_params = 0;
Uint32 sdl_format;
void *d;
@ -366,6 +545,28 @@ on_stream_format_changed(struct data *data, const struct spa_pod *param)
sdl_format = id_to_sdl_format(data->format.info.raw.format);
data->size = data->format.info.raw.size;
if (data->device_negotiation_supported) {
dev_t device_id;
if (find_device_id_from_param(param, &device_id) == 0) {
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
if (major(device_id) == devices[i].major &&
minor(device_id) == devices[i].minor) {
data->used_device_index = i;
printf("using negotiated device %u:%u\n",
devices[i].major, devices[i].minor);
break;
}
}
}
} else {
data->used_device_index = 0;
printf("using implicitly assumed device %u:%u\n",
devices[0].major, devices[0].minor);
}
if (sdl_format == SDL_PIXELFORMAT_UNKNOWN) {
pw_stream_set_error(stream, -EINVAL, "unknown pixel format");
return;
@ -409,8 +610,8 @@ on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param)
return;
switch (id) {
case SPA_PARAM_Tag:
on_stream_tag_changed(data, param);
case SPA_PARAM_PeerCapability:
on_stream_peer_capability_changed(data, param);
break;
case SPA_PARAM_Format:
on_stream_format_changed(data, param);
@ -426,6 +627,22 @@ static const struct pw_stream_events stream_events = {
.process = on_process,
};
static bool
has_device_id(struct data *data, dev_t device_id)
{
size_t i;
if (data->n_device_ids == 0)
return true;
for (i = 0; i < data->n_device_ids; i++) {
if (data->device_ids[i] == device_id)
return true;
}
return false;
}
static int build_formats(struct data *data, struct spa_pod_builder *b, const struct spa_pod **params)
{
SDL_RendererInfo info;
@ -433,13 +650,28 @@ static int build_formats(struct data *data, struct spa_pod_builder *b, const str
SDL_GetRendererInfo(data->renderer, &info);
if (data->mod_info[0].n_modifiers > 0) {
params[n_params++] = build_format(b,
&info, SPA_VIDEO_FORMAT_RGBA,
data->mod_info[0].modifiers,
data->mod_info[0].n_modifiers);
if (data->device_negotiation_supported) {
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
dev_t device_id;
device_id = makedev(devices[i].major, devices[i].minor);
if (!has_device_id(data, device_id)) {
fprintf(stderr, "filtered out %u:%u\n",
devices[i].major, devices[i].minor);
continue;
}
params[n_params++] = build_format(data, b,
&info, SPA_VIDEO_FORMAT_RGBA, i);
}
} else {
params[n_params++] = build_format(data, b,
&info, SPA_VIDEO_FORMAT_RGBA, 0);
}
params[n_params++] = build_format(b, &info, SPA_VIDEO_FORMAT_RGBA, NULL, 0);
params[n_params++] = build_format(data, b, &info, SPA_VIDEO_FORMAT_RGBA, -1);
for (int i=0; i < n_params; i++) {
spa_debug_format(2, NULL, params[i]);
@ -452,7 +684,7 @@ static void reneg_format(void *_data, uint64_t expiration)
struct data *data = (struct data*) _data;
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
const struct spa_pod *params[2];
const struct spa_pod *params[MAX_PARAMS];
uint32_t n_params;
if (data->format.info.raw.format == 0)
@ -473,7 +705,7 @@ static void do_quit(void *userdata, int signal_number)
int main(int argc, char *argv[])
{
struct data data = { 0, };
const struct spa_pod *params[2];
const struct spa_pod *params[MAX_PARAMS];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
struct spa_pod_frame f;
@ -509,6 +741,7 @@ int main(int argc, char *argv[])
/* Set stream target if given on command line */
pw_properties_set(props, PW_KEY_TARGET_OBJECT, data.path);
data.used_device_index = -1;
data.stream = pw_stream_new_simple(
pw_main_loop_get_loop(data.loop),
"video-play-fixate",
@ -521,8 +754,6 @@ int main(int argc, char *argv[])
return -1;
}
init_modifiers(&data);
if (SDL_CreateWindowAndRenderer
(WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) {
fprintf(stderr, "can't create window: %s\n", SDL_GetError());
@ -552,6 +783,13 @@ int main(int argc, char *argv[])
0);
params[n_params++] = spa_pod_builder_pop(&b, &f);
#ifdef SUPPORT_DEVICE_ID_NEGOTIATION
params[n_params++] =
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true")));
#endif
/* now connect the stream, we need a direction (input/output),
* an optional target node to connect to, some flags and parameters
*/
@ -574,8 +812,6 @@ int main(int argc, char *argv[])
pw_stream_destroy(data.stream);
pw_main_loop_destroy(data.loop);
destroy_modifiers(&data);
SDL_DestroyTexture(data.texture);
if (data.cursor)
SDL_DestroyTexture(data.cursor);

View file

@ -18,13 +18,24 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/sysmacros.h>
#include <assert.h>
#include <spa/param/tag-utils.h>
#include <spa/param/dict-utils.h>
#include <spa/param/peer-utils.h>
#include <spa/param/video/format-utils.h>
#include <spa/debug/format.h>
#include <spa/debug/pod.h>
#include <pipewire/pipewire.h>
#include <pipewire/capabilities.h>
#include "base64.h"
/* Comment out to test device ID negotation backward compatibility. */
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
/* Comment out to disable device IDs listing */
#define SUPPORT_DEVICE_IDS_LIST 1
#define BPP 4
#define CURSOR_WIDTH 64
@ -32,10 +43,34 @@
#define CURSOR_BPP 4
#define MAX_BUFFERS 64
#define MAX_PARAMS 16
#define MAX_MOD 10
#define M_PI_M2 ( M_PI + M_PI )
uint64_t supported_modifiers[] = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR};
struct {
unsigned int major;
unsigned int minor;
uint64_t supported_modifiers[MAX_MOD];
size_t n_supported_modifiers;
} devices[] = {
#if 1
{
.major = 100,
.minor = 100,
.supported_modifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR},
.n_supported_modifiers = 2,
},
#endif
#if 1
{
.major = 200,
.minor = 200,
.supported_modifiers = {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_GENERIC_16_16_TILE},
.n_supported_modifiers = 2,
},
#endif
};
struct data {
struct pw_thread_loop *loop;
@ -54,6 +89,7 @@ struct data {
double accumulator;
bool capabilities_known;
bool device_negotiation_supported;
};
static void draw_elipse(uint32_t *dst, int width, int height, uint32_t color)
@ -74,14 +110,24 @@ static void draw_elipse(uint32_t *dst, int width, int height, uint32_t color)
}
}
static struct spa_pod *fixate_format(struct spa_pod_builder *b, enum spa_video_format format,
uint64_t *modifier)
static struct spa_pod *fixate_format(struct data *data, struct spa_pod_builder *b,
int device_index, enum spa_video_format format, uint64_t *modifier)
{
struct spa_pod_frame f[1];
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
/* device */
if (data->device_negotiation_supported) {
dev_t device_id = makedev(devices[device_index].major,
devices[device_index].minor);
spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_deviceId,
SPA_POD_PROP_FLAG_MANDATORY);
spa_pod_builder_bytes(b, &device_id, sizeof device_id);
}
/* format */
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
/* modifiers */
@ -102,15 +148,36 @@ static struct spa_pod *fixate_format(struct spa_pod_builder *b, enum spa_video_f
return spa_pod_builder_pop(b, &f[0]);
}
static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format,
uint64_t *modifiers, int modifier_count)
static struct spa_pod *build_format(struct data *data, struct spa_pod_builder *b,
int device_index, enum spa_video_format format)
{
uint64_t *modifiers;
int modifier_count;
struct spa_pod_frame f[2];
int i, c;
if (device_index == -1) {
modifiers = NULL;
modifier_count = 0;
} else {
modifiers = devices[device_index].supported_modifiers;
modifier_count = devices[device_index].n_supported_modifiers;
}
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
/* device */
if (data->device_negotiation_supported && device_index >= 0) {
dev_t device_id = makedev(devices[device_index].major,
devices[device_index].minor);
spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_deviceId,
SPA_POD_PROP_FLAG_MANDATORY);
spa_pod_builder_bytes(b, &device_id, sizeof device_id);
}
/* format */
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
/* modifiers */
@ -361,15 +428,50 @@ static void on_stream_remove_buffer(void *_data, struct pw_buffer *buffer)
}
static void
on_stream_tag_changed(struct data *data, const struct spa_pod *param)
discover_capabilities(struct data *data, const struct spa_pod *param)
{
#ifdef SUPPORT_DEVICE_ID_NEGOTIATION
struct spa_peer_param_info info;
void *state = NULL;
while (spa_peer_param_parse(param, &info, sizeof(info), &state) == 1) {
struct spa_param_dict_info di;
if (spa_param_dict_parse(info.param, &di, sizeof(di)) > 0) {
struct spa_dict dict;
struct spa_dict_item *items;
const struct spa_dict_item *it;
if (spa_param_dict_info_parse(&di, sizeof(di), &dict, NULL) < 0)
return;
items = alloca(sizeof(struct spa_dict_item) * dict.n_items);
if (spa_param_dict_info_parse(&di, sizeof(di), &dict, items) < 0)
return;
spa_dict_for_each(it, &dict) {
if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION) &&
spa_streq(it->value, "true")) {
data->device_negotiation_supported = true;
}
}
}
}
#endif /* SUPPORT_DEVICE_ID_NEGOTIATION */
}
static void
on_stream_peer_capability_changed(struct data *data, const struct spa_pod *param)
{
struct pw_stream *stream = data->stream;
printf("tag param changed: \n");
printf("peer capability param changed: \n");
spa_debug_pod(4, NULL, param);
discover_capabilities(data, param);
if (!data->capabilities_known) {
const struct spa_pod *params[2];
const struct spa_pod *params[MAX_PARAMS];
uint32_t n_params = 0;
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
@ -381,9 +483,21 @@ on_stream_tag_changed(struct data *data, const struct spa_pod *param)
* The server will select a format that matches and informs us about this
* in the stream param_changed event.
*/
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA,
supported_modifiers, SPA_N_ELEMENTS(supported_modifiers));
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA, NULL, 0);
if (data->device_negotiation_supported) {
size_t i;
printf("stream supports device negotiation\n");
for (i = 0; i < SPA_N_ELEMENTS(devices); i++)
params[n_params++] = build_format(data, &b, i, SPA_VIDEO_FORMAT_RGBA);
params[n_params++] = build_format(data, &b, -1, SPA_VIDEO_FORMAT_RGBA);
} else {
printf("stream does not support device negotiation\n");
params[n_params++] = build_format(data, &b, 0, SPA_VIDEO_FORMAT_RGBA);
params[n_params++] = build_format(data, &b, -1, SPA_VIDEO_FORMAT_RGBA);
}
printf("announcing starting EnumFormats\n");
for (unsigned int i=0; i < n_params; i++) {
@ -397,6 +511,29 @@ on_stream_tag_changed(struct data *data, const struct spa_pod *param)
}
}
static int
find_device_id_from_param(const struct spa_pod *format, dev_t *device_id)
{
const struct spa_pod_prop *device_prop;
const void *bytes;
uint32_t size;
device_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_deviceId);
if (device_prop == NULL)
return -ENOENT;
if (spa_pod_parse_object (format, SPA_TYPE_OBJECT_Format, NULL,
SPA_FORMAT_VIDEO_deviceId, SPA_POD_OPT_Bytes(&bytes, &size)) < 0)
return -EINVAL;
if (size != sizeof(dev_t))
return -EINVAL;
*device_id = *(dev_t *)bytes;
return 0;
}
/* Be notified when the stream format param changes.
*
* We are now supposed to call pw_stream_update_params() with success or
@ -413,7 +550,7 @@ on_stream_format_changed(struct data *data, const struct spa_pod *param)
struct pw_stream *stream = data->stream;
uint8_t params_buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
const struct spa_pod *params[5];
const struct spa_pod *params[MAX_PARAMS];
uint32_t n_params = 0;
int blocks, size, stride, buffertypes;
@ -438,7 +575,31 @@ on_stream_format_changed(struct data *data, const struct spa_pod *param)
// check if the modifier is fixated
if ((prop_modifier->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) > 0) {
const struct spa_pod *pod_modifier = &prop_modifier->value;
printf("fixating format\n");
int device_index = -1;
size_t i;
if (data->device_negotiation_supported) {
dev_t device_id;
if (find_device_id_from_param(param, &device_id) == 0) {
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
if (major(device_id) == devices[i].major &&
minor(device_id) == devices[i].minor)
device_index = i;
}
}
assert(device_index >= 0);
printf("fixating format using negotiated device %u:%u\n",
devices[device_index].major,
devices[device_index].minor);
} else {
device_index = 0;
printf("fixating format using implicitly assumed device %u:%u\n",
devices[device_index].major,
devices[device_index].minor);
}
uint32_t n_modifiers = SPA_POD_CHOICE_N_VALUES(pod_modifier);
uint64_t *modifiers = SPA_POD_CHOICE_VALUES(pod_modifier);
@ -451,14 +612,17 @@ on_stream_format_changed(struct data *data, const struct spa_pod *param)
modifier = modifiers[rand()%n_modifiers];
}
params[n_params++] = fixate_format(&b, SPA_VIDEO_FORMAT_RGBA, &modifier);
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA,
supported_modifiers, SPA_N_ELEMENTS(supported_modifiers));
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA,
NULL, 0);
params[n_params++] = fixate_format(data, &b, device_index,
SPA_VIDEO_FORMAT_RGBA, &modifier);
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
params[n_params++] = build_format(data, &b, i,
SPA_VIDEO_FORMAT_RGBA);
}
params[n_params++] = build_format(data, &b, -1, SPA_VIDEO_FORMAT_RGBA);
printf("announcing fixated EnumFormats\n");
for (unsigned int i=0; i < 3; i++) {
for (unsigned int i=0; i < n_params; i++) {
spa_debug_format(4, NULL, params[i]);
}
@ -516,8 +680,8 @@ on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param)
return;
switch (id) {
case SPA_PARAM_Tag:
on_stream_tag_changed(data, param);
case SPA_PARAM_PeerCapability:
on_stream_peer_capability_changed(data, param);
break;
case SPA_PARAM_Format:
on_stream_format_changed(data, param);
@ -543,7 +707,7 @@ static void do_quit(void *userdata, int signal_number)
int main(int argc, char *argv[])
{
struct data data = { 0, };
const struct spa_pod *params[2];
const struct spa_pod *params[MAX_PARAMS];
uint32_t n_params = 0;
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
@ -611,6 +775,40 @@ int main(int argc, char *argv[])
0);
params[n_params++] = spa_pod_builder_pop(&b, &f);
#ifdef SUPPORT_DEVICE_ID_NEGOTIATION
#ifdef SUPPORT_DEVICE_IDS_LIST
char *device_ids = NULL;
size_t device_ids_size = 0;
FILE *ms;
size_t i;
ms = open_memstream(&device_ids, &device_ids_size);
fprintf(ms, "[");
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];
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);
}
fprintf(ms, "]");
fclose(ms);
#endif /* SUPPORT_DEVICE_IDS_LIST */
params[n_params++] =
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
&SPA_DICT_ITEMS(SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true"),
#ifdef SUPPORT_DEVICE_IDS_LIST
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)
#endif /* SUPPORT_DEVICE_IDS_LIST */
));
#ifdef SUPPORT_DEVICE_IDS_LIST
free(device_ids);
#endif /* SUPPORT_DEVICE_IDS_LIST */
#endif /* SUPPORT_DEVICE_ID_NEGOTIATION */
/* now connect the stream, we need a direction (input/output),
* an optional target node to connect to, some flags and parameters.
*