mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-16 08:56:45 -05:00
props: improve property introspection
Make a new PropInfo parameter that allows us to iterate all properties. Make some new fields to set labels and names for properties. We will be able to add more things to describe the properties this way. Use the Props param to simply query or set properties. We can now make int enum properties and describe all possible values with labels, we don't need to register types anymore. This then makes it possible to enumerate the v4l2 controls and make them available as control params.
This commit is contained in:
parent
1c19342487
commit
e5e360d5df
13 changed files with 611 additions and 175 deletions
|
|
@ -34,6 +34,7 @@
|
|||
#include <spa/param/video/format-utils.h>
|
||||
#include <spa/param/buffers.h>
|
||||
#include <spa/param/meta.h>
|
||||
#include <spa/param/io.h>
|
||||
|
||||
#include <lib/debug.h>
|
||||
#include <lib/pod.h>
|
||||
|
|
@ -82,6 +83,7 @@ struct type {
|
|||
struct spa_type_command_node command_node;
|
||||
struct spa_type_param_buffers param_buffers;
|
||||
struct spa_type_param_meta param_meta;
|
||||
struct spa_type_param_io param_io;
|
||||
struct spa_type_meta meta;
|
||||
struct spa_type_data data;
|
||||
};
|
||||
|
|
@ -95,8 +97,8 @@ static inline void init_type(struct type *type, struct spa_type_map *map)
|
|||
type->prop_device = spa_type_map_get_id(map, SPA_TYPE_PROPS__device);
|
||||
type->prop_device_name = spa_type_map_get_id(map, SPA_TYPE_PROPS__deviceName);
|
||||
type->prop_device_fd = spa_type_map_get_id(map, SPA_TYPE_PROPS__deviceFd);
|
||||
spa_type_io_map(map, &type->io);
|
||||
spa_type_param_map(map, &type->param);
|
||||
spa_type_meta_map(map, &type->meta);
|
||||
spa_type_data_map(map, &type->data);
|
||||
spa_type_media_type_map(map, &type->media_type);
|
||||
spa_type_media_subtype_map(map, &type->media_subtype);
|
||||
spa_type_media_subtype_video_map(map, &type->media_subtype_video);
|
||||
|
|
@ -104,10 +106,11 @@ static inline void init_type(struct type *type, struct spa_type_map *map)
|
|||
spa_type_video_format_map(map, &type->video_format);
|
||||
spa_type_event_node_map(map, &type->event_node);
|
||||
spa_type_command_node_map(map, &type->command_node);
|
||||
spa_type_param_map(map, &type->param);
|
||||
spa_type_param_buffers_map(map, &type->param_buffers);
|
||||
spa_type_param_meta_map(map, &type->param_meta);
|
||||
spa_type_meta_map(map, &type->meta);
|
||||
spa_type_data_map(map, &type->data);
|
||||
spa_type_io_map(map, &type->io);
|
||||
spa_type_param_io_map(map, &type->param_io);
|
||||
}
|
||||
|
||||
struct port {
|
||||
|
|
@ -129,6 +132,7 @@ struct port {
|
|||
|
||||
int fd;
|
||||
bool opened;
|
||||
bool have_query_ext_ctrl;
|
||||
struct v4l2_capability cap;
|
||||
struct v4l2_format fmt;
|
||||
enum v4l2_buf_type type;
|
||||
|
|
@ -194,23 +198,58 @@ static int impl_node_enum_params(struct spa_node *node,
|
|||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
|
||||
if (id == t->param.idList) {
|
||||
if (*index > 0)
|
||||
return 0;
|
||||
uint32_t list[] = { t->param.idPropInfo,
|
||||
t->param.idProps };
|
||||
|
||||
param = spa_pod_builder_object(&b,
|
||||
id, t->param.List,
|
||||
":", t->param.listId, "I", t->param.idProps);
|
||||
if (*index < SPA_N_ELEMENTS(list))
|
||||
param = spa_pod_builder_object(&b, id, t->param.List,
|
||||
":", t->param.listId, "I", list[*index]);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else if (id == t->param.idPropInfo) {
|
||||
struct props *p = &this->props;
|
||||
|
||||
switch (*index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_object(&b,
|
||||
id, t->param.PropInfo,
|
||||
":", t->param.propId, "I", t->prop_device,
|
||||
":", t->param.propName, "s", "The V4L2 device",
|
||||
":", t->param.propType, "S", p->device, sizeof(p->device));
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_object(&b,
|
||||
id, t->param.PropInfo,
|
||||
":", t->param.propId, "I", t->prop_device_name,
|
||||
":", t->param.propName, "s", "The V4L2 device name",
|
||||
":", t->param.propType, "S-r", p->device_name, sizeof(p->device_name));
|
||||
break;
|
||||
case 2:
|
||||
param = spa_pod_builder_object(&b,
|
||||
id, t->param.PropInfo,
|
||||
":", t->param.propId, "I", t->prop_device_fd,
|
||||
":", t->param.propName, "s", "The V4L2 fd",
|
||||
":", t->param.propType, "i-r", p->device_fd);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (id == t->param.idProps) {
|
||||
struct props *p = &this->props;
|
||||
|
||||
if (*index > 0)
|
||||
switch (*index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_object(&b,
|
||||
id, t->props,
|
||||
":", t->prop_device, "S", p->device, sizeof(p->device),
|
||||
":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name),
|
||||
":", t->prop_device_fd, "i-r", p->device_fd);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
||||
param = spa_pod_builder_object(&b, t->param.idProps, t->props,
|
||||
":", t->prop_device, "S", p->device, sizeof(p->device),
|
||||
":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name),
|
||||
":", t->prop_device_fd, "i-r", p->device_fd);
|
||||
}
|
||||
}
|
||||
else
|
||||
return -ENOENT;
|
||||
|
|
@ -547,7 +586,8 @@ static int impl_node_port_enum_params(struct spa_node *node,
|
|||
uint32_t list[] = { t->param.idEnumFormat,
|
||||
t->param.idFormat,
|
||||
t->param.idBuffers,
|
||||
t->param.idMeta };
|
||||
t->param.idMeta,
|
||||
t->param_io.idPropsIn };
|
||||
|
||||
if (*index < SPA_N_ELEMENTS(list))
|
||||
param = spa_pod_builder_object(&b, id, t->param.List,
|
||||
|
|
@ -588,6 +628,9 @@ static int impl_node_port_enum_params(struct spa_node *node,
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
else if (id == t->param_io.idPropsIn) {
|
||||
return spa_v4l2_enum_controls(this, index, filter, result, builder);
|
||||
}
|
||||
else
|
||||
return -ENOENT;
|
||||
|
||||
|
|
@ -1010,6 +1053,7 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->out_ports[0].info.flags = SPA_PORT_INFO_FLAG_LIVE;
|
||||
|
||||
this->out_ports[0].export_buf = true;
|
||||
this->out_ports[0].have_query_ext_ctrl = true;
|
||||
|
||||
if (info && (str = spa_dict_lookup(info, "device.path"))) {
|
||||
strncpy(this->props.device, str, 63);
|
||||
|
|
|
|||
|
|
@ -821,7 +821,7 @@ spa_v4l2_enum_format(struct impl *this,
|
|||
static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, bool try_only)
|
||||
{
|
||||
struct port *port = &this->out_ports[0];
|
||||
int cmd;
|
||||
int res, cmd;
|
||||
struct v4l2_format reqfmt, fmt;
|
||||
struct v4l2_streamparm streamparm;
|
||||
const struct format_info *info = NULL;
|
||||
|
|
@ -858,7 +858,7 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
|
|||
if (info == NULL || size == NULL || framerate == NULL) {
|
||||
spa_log_error(port->log, "v4l2: unknown media type %d %d %d", format->media_type,
|
||||
format->media_subtype, video_format);
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -876,13 +876,14 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
|
|||
|
||||
reqfmt = fmt;
|
||||
|
||||
if (spa_v4l2_open(this) < 0)
|
||||
return -1;
|
||||
if ((res = spa_v4l2_open(this)) < 0)
|
||||
return res;
|
||||
|
||||
cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
|
||||
if (xioctl(port->fd, cmd, &fmt) < 0) {
|
||||
res = -errno;
|
||||
perror("VIDIOC_S_FMT");
|
||||
return -1;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* some cheap USB cam's won't accept any change */
|
||||
|
|
@ -897,7 +898,7 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
|
|||
if (reqfmt.fmt.pix.pixelformat != fmt.fmt.pix.pixelformat ||
|
||||
reqfmt.fmt.pix.width != fmt.fmt.pix.width ||
|
||||
reqfmt.fmt.pix.height != fmt.fmt.pix.height)
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
|
||||
if (try_only)
|
||||
return 0;
|
||||
|
|
@ -915,6 +916,185 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int query_ext_ctrl_ioctl(struct port *port, struct v4l2_query_ext_ctrl *qctrl)
|
||||
{
|
||||
struct v4l2_queryctrl qc;
|
||||
int res;
|
||||
|
||||
if (port->have_query_ext_ctrl) {
|
||||
res = ioctl(port->fd, VIDIOC_QUERY_EXT_CTRL, qctrl);
|
||||
if (errno != ENOTTY)
|
||||
return res;
|
||||
port->have_query_ext_ctrl = false;
|
||||
}
|
||||
qc.id = qctrl->id;
|
||||
res = ioctl(port->fd, VIDIOC_QUERYCTRL, &qc);
|
||||
if (res == 0) {
|
||||
qctrl->type = qc.type;
|
||||
memcpy(qctrl->name, qc.name, sizeof(qctrl->name));
|
||||
qctrl->minimum = qc.minimum;
|
||||
if (qc.type == V4L2_CTRL_TYPE_BITMASK) {
|
||||
qctrl->maximum = (__u32)qc.maximum;
|
||||
qctrl->default_value = (__u32)qc.default_value;
|
||||
} else {
|
||||
qctrl->maximum = qc.maximum;
|
||||
qctrl->default_value = qc.default_value;
|
||||
}
|
||||
qctrl->step = qc.step;
|
||||
qctrl->flags = qc.flags;
|
||||
qctrl->elems = 1;
|
||||
qctrl->nr_of_dims = 0;
|
||||
memset(qctrl->dims, 0, sizeof(qctrl->dims));
|
||||
switch (qctrl->type) {
|
||||
case V4L2_CTRL_TYPE_INTEGER64:
|
||||
qctrl->elem_size = sizeof(__s64);
|
||||
break;
|
||||
case V4L2_CTRL_TYPE_STRING:
|
||||
qctrl->elem_size = qc.maximum + 1;
|
||||
break;
|
||||
default:
|
||||
qctrl->elem_size = sizeof(__s32);
|
||||
break;
|
||||
}
|
||||
memset(qctrl->reserved, 0, sizeof(qctrl->reserved));
|
||||
}
|
||||
qctrl->id = qc.id;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_v4l2_enum_controls(struct impl *this,
|
||||
uint32_t *index,
|
||||
const struct spa_pod *filter,
|
||||
struct spa_pod **result,
|
||||
struct spa_pod_builder *builder)
|
||||
{
|
||||
struct port *port = &this->out_ports[0];
|
||||
struct type *t = &this->type;
|
||||
struct v4l2_query_ext_ctrl queryctrl;
|
||||
struct spa_pod *param;
|
||||
struct spa_pod_builder b = { 0 };
|
||||
uint8_t buffer[1024];
|
||||
int res;
|
||||
const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
|
||||
|
||||
if ((res = spa_v4l2_open(this)) < 0)
|
||||
return res;
|
||||
|
||||
next:
|
||||
spa_zero(queryctrl);
|
||||
|
||||
if (*index == 0)
|
||||
*index |= next_fl;
|
||||
|
||||
queryctrl.id = *index;
|
||||
spa_log_debug(port->log, "test control %08x", queryctrl.id);
|
||||
|
||||
if (query_ext_ctrl_ioctl(port, &queryctrl) != 0) {
|
||||
if (errno == EINVAL) {
|
||||
if (queryctrl.id != next_fl)
|
||||
goto enum_end;
|
||||
|
||||
if (*index & next_fl)
|
||||
*index = V4L2_CID_USER_BASE;
|
||||
else if (*index >= V4L2_CID_USER_BASE && *index < V4L2_CID_LASTP1)
|
||||
(*index)++;
|
||||
else if (*index >= V4L2_CID_LASTP1)
|
||||
*index = V4L2_CID_PRIVATE_BASE;
|
||||
else
|
||||
goto enum_end;
|
||||
goto next;
|
||||
}
|
||||
res = -errno;
|
||||
perror("VIDIOC_QUERYCTRL");
|
||||
return res;
|
||||
}
|
||||
if (*index & next_fl)
|
||||
(*index) = queryctrl.id | next_fl;
|
||||
else
|
||||
(*index)++;
|
||||
|
||||
if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
|
||||
goto next;
|
||||
|
||||
spa_log_debug(port->log, "Control %s", queryctrl.name);
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
|
||||
switch (queryctrl.type) {
|
||||
case V4L2_CTRL_TYPE_INTEGER:
|
||||
param = spa_pod_builder_object(&b,
|
||||
t->param_io.idPropsIn, t->param_io.Prop,
|
||||
":", t->param_io.size, "i", sizeof(struct spa_pod_int),
|
||||
":", t->param.propType, "isu", queryctrl.default_value,
|
||||
3, queryctrl.minimum,
|
||||
queryctrl.maximum,
|
||||
queryctrl.step,
|
||||
":", t->param.propName, "s", queryctrl.name);
|
||||
break;
|
||||
case V4L2_CTRL_TYPE_BOOLEAN:
|
||||
param = spa_pod_builder_object(&b,
|
||||
t->param_io.idPropsIn, t->param_io.Prop,
|
||||
":", t->param_io.size, "i", sizeof(struct spa_pod_bool),
|
||||
":", t->param.propType, "b-u", queryctrl.default_value,
|
||||
":", t->param.propName, "s", queryctrl.name);
|
||||
break;
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
{
|
||||
struct v4l2_querymenu querymenu;
|
||||
|
||||
spa_pod_builder_push_object(&b, t->param_io.idPropsIn, t->param_io.Prop);
|
||||
spa_pod_builder_add(&b,
|
||||
":", t->param_io.size, "i", sizeof(struct spa_pod_int),
|
||||
":", t->param.propName, "s", queryctrl.name,
|
||||
NULL);
|
||||
|
||||
spa_pod_builder_push_prop(&b, t->param.propType, SPA_POD_PROP_FLAG_UNSET);
|
||||
spa_pod_builder_int(&b, queryctrl.default_value);
|
||||
spa_pod_builder_pop(&b);
|
||||
|
||||
spa_zero(querymenu);
|
||||
querymenu.id = queryctrl.id;
|
||||
|
||||
spa_pod_builder_push_prop(&b, t->param.propLabels, 0);
|
||||
spa_pod_builder_push_struct(&b);
|
||||
for (querymenu.index = queryctrl.minimum;
|
||||
querymenu.index <= queryctrl.maximum;
|
||||
querymenu.index++) {
|
||||
if (ioctl(port->fd, VIDIOC_QUERYMENU, &querymenu) == 0) {
|
||||
spa_pod_builder_int(&b, querymenu.index);
|
||||
spa_pod_builder_string(&b, (const char *)querymenu.name);
|
||||
}
|
||||
}
|
||||
spa_pod_builder_pop(&b);
|
||||
spa_pod_builder_pop(&b);
|
||||
param = spa_pod_builder_pop(&b);
|
||||
break;
|
||||
}
|
||||
case V4L2_CTRL_TYPE_INTEGER_MENU:
|
||||
case V4L2_CTRL_TYPE_BITMASK:
|
||||
case V4L2_CTRL_TYPE_BUTTON:
|
||||
case V4L2_CTRL_TYPE_INTEGER64:
|
||||
case V4L2_CTRL_TYPE_STRING:
|
||||
default:
|
||||
goto next;
|
||||
|
||||
}
|
||||
if (spa_pod_filter(builder, result, param, filter) < 0)
|
||||
goto next;
|
||||
|
||||
res = 1;
|
||||
|
||||
exit:
|
||||
spa_v4l2_close(this);
|
||||
|
||||
return res;
|
||||
|
||||
enum_end:
|
||||
res = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
static int mmap_read(struct impl *this)
|
||||
{
|
||||
struct port *port = &this->out_ports[0];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue