mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-02-05 00:40:29 +01:00
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:
parent
5481a235ed
commit
f055cf398d
3 changed files with 544 additions and 64 deletions
46
src/examples/base64.h
Normal file
46
src/examples/base64.h
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue