mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-03-01 01:40:29 -05: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 <signal.h>
|
||||||
#include <libdrm/drm_fourcc.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <spa/utils/json.h>
|
||||||
#include <spa/utils/result.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/video/format-utils.h>
|
||||||
#include <spa/param/props.h>
|
#include <spa/param/props.h>
|
||||||
#include <spa/debug/format.h>
|
#include <spa/debug/format.h>
|
||||||
#include <spa/debug/pod.h>
|
#include <spa/debug/pod.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.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. */
|
/* If defined, emulate failing to import DMA buffer. */
|
||||||
#define EMULATE_DMA_BUF_IMPORT_FAIL 1
|
#define EMULATE_DMA_BUF_IMPORT_FAIL 1
|
||||||
|
|
||||||
|
|
@ -32,6 +42,8 @@
|
||||||
|
|
||||||
#define MAX_BUFFERS 64
|
#define MAX_BUFFERS 64
|
||||||
#define MAX_MOD 8
|
#define MAX_MOD 8
|
||||||
|
#define MAX_PARAMS 16
|
||||||
|
#define MAX_DEVICE_IDS 16
|
||||||
|
|
||||||
#include "sdl.h"
|
#include "sdl.h"
|
||||||
|
|
||||||
|
|
@ -51,6 +63,35 @@ struct modifier_info {
|
||||||
uint64_t modifiers[MAX_MOD];
|
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 {
|
struct data {
|
||||||
const char *path;
|
const char *path;
|
||||||
|
|
||||||
|
|
@ -69,16 +110,20 @@ struct data {
|
||||||
int32_t stride;
|
int32_t stride;
|
||||||
struct spa_rectangle size;
|
struct spa_rectangle size;
|
||||||
|
|
||||||
uint32_t n_mod_info;
|
|
||||||
struct modifier_info mod_info[2];
|
|
||||||
|
|
||||||
int counter;
|
int counter;
|
||||||
|
|
||||||
bool capabilities_known;
|
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);
|
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) {
|
static struct pw_version parse_pw_version(const char* version) {
|
||||||
struct pw_version pw_version;
|
struct pw_version pw_version;
|
||||||
sscanf(version, "%d.%d.%d", &pw_version.major, &pw_version.minor,
|
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;
|
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)
|
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;
|
return;
|
||||||
struct modifier_info *mod_info = &data->mod_info[0];
|
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
// Dropping of single modifiers is only supported on PipeWire 0.3.40 and newer.
|
// 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
|
// On older PipeWire just dropping all modifiers might work on Versions newer then 0.3.33/35
|
||||||
if (has_pw_version(0,3,40)) {
|
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++) {
|
for (uint32_t i = 0; i < mod_info->n_modifiers; i++) {
|
||||||
if (mod_info->modifiers[i] == modifier)
|
if (mod_info->modifiers[i] == modifier)
|
||||||
continue;
|
continue;
|
||||||
mod_info->modifiers[counter++] = mod_info->modifiers[i];
|
mod_info->modifiers[counter++] = mod_info->modifiers[i];
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
counter = 0;
|
||||||
}
|
}
|
||||||
mod_info->n_modifiers = counter;
|
mod_info->n_modifiers = counter;
|
||||||
}
|
}
|
||||||
|
#endif /* EMULATE_DMA_BUF_IMPORT_FAIL */
|
||||||
|
|
||||||
static void handle_events(struct data *data)
|
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,
|
static struct spa_pod *build_format(struct data *data, struct spa_pod_builder *b,
|
||||||
uint64_t *modifiers, int modifier_count)
|
SDL_RendererInfo *info, enum spa_video_format format, int device_index)
|
||||||
{
|
{
|
||||||
struct spa_pod_frame f[2];
|
struct spa_pod_frame f[2];
|
||||||
int i, c;
|
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_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_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);
|
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 */
|
/* format */
|
||||||
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
||||||
/* modifiers */
|
/* modifiers */
|
||||||
|
|
@ -303,25 +366,118 @@ static void on_stream_state_changed(void *_data, enum pw_stream_state old,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
struct pw_stream *stream = data->stream;
|
||||||
|
|
||||||
printf("tag param changed: \n");
|
printf("peer capability param changed: \n");
|
||||||
spa_debug_pod(4, NULL, param);
|
spa_debug_pod(4, NULL, param);
|
||||||
|
|
||||||
|
discover_capabilities(data, param);
|
||||||
|
|
||||||
if (!data->capabilities_known) {
|
if (!data->capabilities_known) {
|
||||||
const struct spa_pod *params[2];
|
const struct spa_pod *params[MAX_PARAMS];
|
||||||
uint32_t n_params = 0;
|
uint32_t n_params = 0;
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
|
|
||||||
data->capabilities_known = true;
|
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
|
/* 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
|
* a list of supported formats. We use a builder that writes the param
|
||||||
* object to the stack. */
|
* object to the stack. */
|
||||||
printf("supported formats:\n");
|
printf("supported formats:\n");
|
||||||
|
|
||||||
n_params = build_formats(data, &b, params);
|
n_params = build_formats(data, &b, params);
|
||||||
pw_stream_update_params(data->stream, params, n_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.
|
/* Be notified when the stream format param changes.
|
||||||
*
|
*
|
||||||
* We are now supposed to call pw_stream_finish_format() with success or
|
* 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;
|
struct pw_stream *stream = data->stream;
|
||||||
uint8_t params_buffer[1024];
|
uint8_t params_buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
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_t n_params = 0;
|
||||||
Uint32 sdl_format;
|
Uint32 sdl_format;
|
||||||
void *d;
|
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);
|
sdl_format = id_to_sdl_format(data->format.info.raw.format);
|
||||||
data->size = data->format.info.raw.size;
|
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) {
|
if (sdl_format == SDL_PIXELFORMAT_UNKNOWN) {
|
||||||
pw_stream_set_error(stream, -EINVAL, "unknown pixel format");
|
pw_stream_set_error(stream, -EINVAL, "unknown pixel format");
|
||||||
return;
|
return;
|
||||||
|
|
@ -409,8 +610,8 @@ on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case SPA_PARAM_Tag:
|
case SPA_PARAM_PeerCapability:
|
||||||
on_stream_tag_changed(data, param);
|
on_stream_peer_capability_changed(data, param);
|
||||||
break;
|
break;
|
||||||
case SPA_PARAM_Format:
|
case SPA_PARAM_Format:
|
||||||
on_stream_format_changed(data, param);
|
on_stream_format_changed(data, param);
|
||||||
|
|
@ -426,6 +627,22 @@ static const struct pw_stream_events stream_events = {
|
||||||
.process = on_process,
|
.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)
|
static int build_formats(struct data *data, struct spa_pod_builder *b, const struct spa_pod **params)
|
||||||
{
|
{
|
||||||
SDL_RendererInfo info;
|
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);
|
SDL_GetRendererInfo(data->renderer, &info);
|
||||||
|
|
||||||
if (data->mod_info[0].n_modifiers > 0) {
|
if (data->device_negotiation_supported) {
|
||||||
params[n_params++] = build_format(b,
|
size_t i;
|
||||||
&info, SPA_VIDEO_FORMAT_RGBA,
|
|
||||||
data->mod_info[0].modifiers,
|
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
|
||||||
data->mod_info[0].n_modifiers);
|
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++) {
|
for (int i=0; i < n_params; i++) {
|
||||||
spa_debug_format(2, NULL, 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;
|
struct data *data = (struct data*) _data;
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
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;
|
uint32_t n_params;
|
||||||
|
|
||||||
if (data->format.info.raw.format == 0)
|
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[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct data data = { 0, };
|
struct data data = { 0, };
|
||||||
const struct spa_pod *params[2];
|
const struct spa_pod *params[MAX_PARAMS];
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
struct spa_pod_frame f;
|
struct spa_pod_frame f;
|
||||||
|
|
@ -509,6 +741,7 @@ int main(int argc, char *argv[])
|
||||||
/* Set stream target if given on command line */
|
/* Set stream target if given on command line */
|
||||||
pw_properties_set(props, PW_KEY_TARGET_OBJECT, data.path);
|
pw_properties_set(props, PW_KEY_TARGET_OBJECT, data.path);
|
||||||
|
|
||||||
|
data.used_device_index = -1;
|
||||||
data.stream = pw_stream_new_simple(
|
data.stream = pw_stream_new_simple(
|
||||||
pw_main_loop_get_loop(data.loop),
|
pw_main_loop_get_loop(data.loop),
|
||||||
"video-play-fixate",
|
"video-play-fixate",
|
||||||
|
|
@ -521,8 +754,6 @@ int main(int argc, char *argv[])
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_modifiers(&data);
|
|
||||||
|
|
||||||
if (SDL_CreateWindowAndRenderer
|
if (SDL_CreateWindowAndRenderer
|
||||||
(WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) {
|
(WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) {
|
||||||
fprintf(stderr, "can't create window: %s\n", SDL_GetError());
|
fprintf(stderr, "can't create window: %s\n", SDL_GetError());
|
||||||
|
|
@ -552,6 +783,13 @@ int main(int argc, char *argv[])
|
||||||
0);
|
0);
|
||||||
params[n_params++] = spa_pod_builder_pop(&b, &f);
|
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),
|
/* now connect the stream, we need a direction (input/output),
|
||||||
* an optional target node to connect to, some flags and parameters
|
* 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_stream_destroy(data.stream);
|
||||||
pw_main_loop_destroy(data.loop);
|
pw_main_loop_destroy(data.loop);
|
||||||
|
|
||||||
destroy_modifiers(&data);
|
|
||||||
|
|
||||||
SDL_DestroyTexture(data.texture);
|
SDL_DestroyTexture(data.texture);
|
||||||
if (data.cursor)
|
if (data.cursor)
|
||||||
SDL_DestroyTexture(data.cursor);
|
SDL_DestroyTexture(data.cursor);
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,24 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/mman.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/param/video/format-utils.h>
|
||||||
#include <spa/debug/format.h>
|
#include <spa/debug/format.h>
|
||||||
#include <spa/debug/pod.h>
|
#include <spa/debug/pod.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.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 BPP 4
|
||||||
#define CURSOR_WIDTH 64
|
#define CURSOR_WIDTH 64
|
||||||
|
|
@ -32,10 +43,34 @@
|
||||||
#define CURSOR_BPP 4
|
#define CURSOR_BPP 4
|
||||||
|
|
||||||
#define MAX_BUFFERS 64
|
#define MAX_BUFFERS 64
|
||||||
|
#define MAX_PARAMS 16
|
||||||
|
#define MAX_MOD 10
|
||||||
|
|
||||||
#define M_PI_M2 ( M_PI + M_PI )
|
#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 data {
|
||||||
struct pw_thread_loop *loop;
|
struct pw_thread_loop *loop;
|
||||||
|
|
@ -54,6 +89,7 @@ struct data {
|
||||||
double accumulator;
|
double accumulator;
|
||||||
|
|
||||||
bool capabilities_known;
|
bool capabilities_known;
|
||||||
|
bool device_negotiation_supported;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void draw_elipse(uint32_t *dst, int width, int height, uint32_t color)
|
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,
|
static struct spa_pod *fixate_format(struct data *data, struct spa_pod_builder *b,
|
||||||
uint64_t *modifier)
|
int device_index, enum spa_video_format format, uint64_t *modifier)
|
||||||
{
|
{
|
||||||
struct spa_pod_frame f[1];
|
struct spa_pod_frame f[1];
|
||||||
|
|
||||||
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
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_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);
|
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 */
|
/* format */
|
||||||
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
||||||
/* modifiers */
|
/* 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]);
|
return spa_pod_builder_pop(b, &f[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format,
|
static struct spa_pod *build_format(struct data *data, struct spa_pod_builder *b,
|
||||||
uint64_t *modifiers, int modifier_count)
|
int device_index, enum spa_video_format format)
|
||||||
{
|
{
|
||||||
|
uint64_t *modifiers;
|
||||||
|
int modifier_count;
|
||||||
struct spa_pod_frame f[2];
|
struct spa_pod_frame f[2];
|
||||||
int i, c;
|
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_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_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);
|
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 */
|
/* format */
|
||||||
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
||||||
/* modifiers */
|
/* modifiers */
|
||||||
|
|
@ -361,15 +428,50 @@ static void on_stream_remove_buffer(void *_data, struct pw_buffer *buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
struct pw_stream *stream = data->stream;
|
||||||
|
|
||||||
printf("tag param changed: \n");
|
printf("peer capability param changed: \n");
|
||||||
spa_debug_pod(4, NULL, param);
|
spa_debug_pod(4, NULL, param);
|
||||||
|
|
||||||
|
discover_capabilities(data, param);
|
||||||
|
|
||||||
if (!data->capabilities_known) {
|
if (!data->capabilities_known) {
|
||||||
const struct spa_pod *params[2];
|
const struct spa_pod *params[MAX_PARAMS];
|
||||||
uint32_t n_params = 0;
|
uint32_t n_params = 0;
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
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
|
* The server will select a format that matches and informs us about this
|
||||||
* in the stream param_changed event.
|
* in the stream param_changed event.
|
||||||
*/
|
*/
|
||||||
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA,
|
|
||||||
supported_modifiers, SPA_N_ELEMENTS(supported_modifiers));
|
if (data->device_negotiation_supported) {
|
||||||
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA, NULL, 0);
|
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");
|
printf("announcing starting EnumFormats\n");
|
||||||
for (unsigned int i=0; i < n_params; i++) {
|
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.
|
/* Be notified when the stream format param changes.
|
||||||
*
|
*
|
||||||
* We are now supposed to call pw_stream_update_params() with success or
|
* 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;
|
struct pw_stream *stream = data->stream;
|
||||||
uint8_t params_buffer[1024];
|
uint8_t params_buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
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;
|
uint32_t n_params = 0;
|
||||||
int blocks, size, stride, buffertypes;
|
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
|
// check if the modifier is fixated
|
||||||
if ((prop_modifier->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) > 0) {
|
if ((prop_modifier->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) > 0) {
|
||||||
const struct spa_pod *pod_modifier = &prop_modifier->value;
|
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);
|
uint32_t n_modifiers = SPA_POD_CHOICE_N_VALUES(pod_modifier);
|
||||||
uint64_t *modifiers = SPA_POD_CHOICE_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];
|
modifier = modifiers[rand()%n_modifiers];
|
||||||
}
|
}
|
||||||
|
|
||||||
params[n_params++] = fixate_format(&b, SPA_VIDEO_FORMAT_RGBA, &modifier);
|
params[n_params++] = fixate_format(data, &b, device_index,
|
||||||
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA,
|
SPA_VIDEO_FORMAT_RGBA, &modifier);
|
||||||
supported_modifiers, SPA_N_ELEMENTS(supported_modifiers));
|
|
||||||
params[n_params++] = build_format(&b, SPA_VIDEO_FORMAT_RGBA,
|
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
|
||||||
NULL, 0);
|
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");
|
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]);
|
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;
|
return;
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case SPA_PARAM_Tag:
|
case SPA_PARAM_PeerCapability:
|
||||||
on_stream_tag_changed(data, param);
|
on_stream_peer_capability_changed(data, param);
|
||||||
break;
|
break;
|
||||||
case SPA_PARAM_Format:
|
case SPA_PARAM_Format:
|
||||||
on_stream_format_changed(data, param);
|
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[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct data data = { 0, };
|
struct data data = { 0, };
|
||||||
const struct spa_pod *params[2];
|
const struct spa_pod *params[MAX_PARAMS];
|
||||||
uint32_t n_params = 0;
|
uint32_t n_params = 0;
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
|
|
@ -611,6 +775,40 @@ int main(int argc, char *argv[])
|
||||||
0);
|
0);
|
||||||
params[n_params++] = spa_pod_builder_pop(&b, &f);
|
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),
|
/* now connect the stream, we need a direction (input/output),
|
||||||
* an optional target node to connect to, some flags and parameters.
|
* an optional target node to connect to, some flags and parameters.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue