diff --git a/spa/include/spa/param/props.h b/spa/include/spa/param/props.h index 2e3bffee9..b64ef3412 100644 --- a/spa/include/spa/param/props.h +++ b/spa/include/spa/param/props.h @@ -29,17 +29,22 @@ extern "C" { #define SPA_TYPE__Props SPA_TYPE_PARAM_BASE "Props" #define SPA_TYPE_PROPS_BASE SPA_TYPE__Props ":" +/** an unknown property */ +#define SPA_TYPE_PROPS__unknown SPA_TYPE_PROPS_BASE "unknown" + /** Common property ids */ #define SPA_TYPE_PROPS__device SPA_TYPE_PROPS_BASE "device" #define SPA_TYPE_PROPS__deviceName SPA_TYPE_PROPS_BASE "deviceName" #define SPA_TYPE_PROPS__deviceFd SPA_TYPE_PROPS_BASE "deviceFd" #define SPA_TYPE_PROPS__card SPA_TYPE_PROPS_BASE "card" #define SPA_TYPE_PROPS__cardName SPA_TYPE_PROPS_BASE "cardName" + #define SPA_TYPE_PROPS__minLatency SPA_TYPE_PROPS_BASE "minLatency" #define SPA_TYPE_PROPS__maxLatency SPA_TYPE_PROPS_BASE "maxLatency" #define SPA_TYPE_PROPS__periods SPA_TYPE_PROPS_BASE "periods" #define SPA_TYPE_PROPS__periodSize SPA_TYPE_PROPS_BASE "periodSize" #define SPA_TYPE_PROPS__periodEvent SPA_TYPE_PROPS_BASE "periodEvent" + #define SPA_TYPE_PROPS__live SPA_TYPE_PROPS_BASE "live" #define SPA_TYPE_PROPS__waveType SPA_TYPE_PROPS_BASE "waveType" #define SPA_TYPE_PROPS__frequency SPA_TYPE_PROPS_BASE "frequency" @@ -47,6 +52,15 @@ extern "C" { #define SPA_TYPE_PROPS__mute SPA_TYPE_PROPS_BASE "mute" #define SPA_TYPE_PROPS__patternType SPA_TYPE_PROPS_BASE "patternType" +#define SPA_TYPE_PROPS__brightness SPA_TYPE_PROPS_BASE "brightness" +#define SPA_TYPE_PROPS__contrast SPA_TYPE_PROPS_BASE "contrast" +#define SPA_TYPE_PROPS__saturation SPA_TYPE_PROPS_BASE "saturation" +#define SPA_TYPE_PROPS__hue SPA_TYPE_PROPS_BASE "hue" +#define SPA_TYPE_PROPS__gamma SPA_TYPE_PROPS_BASE "gamma" +#define SPA_TYPE_PROPS__exposure SPA_TYPE_PROPS_BASE "exposure" +#define SPA_TYPE_PROPS__gain SPA_TYPE_PROPS_BASE "gain" +#define SPA_TYPE_PROPS__sharpness SPA_TYPE_PROPS_BASE "sharpness" + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index 0e5cd1cb1..379e26d2b 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -69,9 +69,18 @@ struct type { uint32_t clock; uint32_t format; uint32_t props; + uint32_t prop_unknown; uint32_t prop_device; uint32_t prop_device_name; uint32_t prop_device_fd; + uint32_t prop_brightness; + uint32_t prop_contrast; + uint32_t prop_saturation; + uint32_t prop_hue; + uint32_t prop_gamma; + uint32_t prop_exposure; + uint32_t prop_gain; + uint32_t prop_sharpness; struct spa_type_io io; struct spa_type_param param; struct spa_type_media_type media_type; @@ -94,9 +103,18 @@ static inline void init_type(struct type *type, struct spa_type_map *map) type->clock = spa_type_map_get_id(map, SPA_TYPE__Clock); type->format = spa_type_map_get_id(map, SPA_TYPE__Format); type->props = spa_type_map_get_id(map, SPA_TYPE__Props); + type->prop_unknown = spa_type_map_get_id(map, SPA_TYPE_PROPS__unknown); 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); + type->prop_brightness = spa_type_map_get_id(map, SPA_TYPE_PROPS__brightness); + type->prop_contrast = spa_type_map_get_id(map, SPA_TYPE_PROPS__contrast); + type->prop_saturation = spa_type_map_get_id(map, SPA_TYPE_PROPS__saturation); + type->prop_hue = spa_type_map_get_id(map, SPA_TYPE_PROPS__hue); + type->prop_gamma = spa_type_map_get_id(map, SPA_TYPE_PROPS__gamma); + type->prop_exposure = spa_type_map_get_id(map, SPA_TYPE_PROPS__exposure); + type->prop_gain = spa_type_map_get_id(map, SPA_TYPE_PROPS__gain); + type->prop_sharpness = spa_type_map_get_id(map, SPA_TYPE_PROPS__sharpness); spa_type_meta_map(map, &type->meta); spa_type_data_map(map, &type->data); spa_type_media_type_map(map, &type->media_type); @@ -113,6 +131,15 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_param_io_map(map, &type->param_io); } +#define MAX_CONTROLS 64 + +struct control { + uint32_t id; + uint32_t ctrl_id; + double value; + double *io; +}; + struct port { struct spa_log *log; struct spa_loop *main_loop; @@ -138,6 +165,9 @@ struct port { enum v4l2_buf_type type; enum v4l2_memory memtype; + struct control controls[MAX_CONTROLS]; + uint32_t n_controls; + struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -171,6 +201,8 @@ struct impl { #define CHECK_PORT(this,direction,port_id) ((direction) == SPA_DIRECTION_OUTPUT && (port_id) == 0) +#define GET_OUT_PORT(this,p) (&this->out_ports[p]) +#define GET_PORT(this,d,p) GET_OUT_PORT(this,p) #include "v4l2-utils.c" @@ -300,7 +332,7 @@ static int impl_node_send_command(struct spa_node *node, const struct spa_comman this = SPA_CONTAINER_OF(node, struct impl, node); if (SPA_COMMAND_TYPE(command) == this->type.command_node.Start) { - struct port *port = &this->out_ports[0]; + struct port *port = GET_OUT_PORT(this, 0); if (!port->have_format) return -EIO; @@ -399,7 +431,7 @@ static int impl_node_port_get_info(struct spa_node *node, spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - *info = &this->out_ports[port_id].info; + *info = &GET_PORT(this, direction, port_id)->info; return 0; } @@ -413,7 +445,7 @@ static int port_get_format(struct spa_node *node, { struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); struct type *t = &this->type; - struct port *port = &this->out_ports[port_id]; + struct port *port = GET_PORT(this, direction, port_id); if (!port->have_format) return -EIO; @@ -474,7 +506,7 @@ static int impl_node_port_enum_params(struct spa_node *node, spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->out_ports[port_id]; + port = GET_PORT(this, direction, port_id); next: spa_pod_builder_init(&b, buffer, sizeof(buffer)); @@ -547,7 +579,7 @@ static int port_set_format(struct spa_node *node, struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); struct spa_video_info info; struct type *t = &this->type; - struct port *port = &this->out_ports[port_id]; + struct port *port = GET_PORT(this, direction, port_id); if (format == NULL) { spa_v4l2_stream_off(this); @@ -652,7 +684,7 @@ static int impl_node_port_use_buffers(struct spa_node *node, spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->out_ports[port_id]; + port = GET_PORT(this, direction, port_id); if (!port->have_format) return -EIO; @@ -689,7 +721,7 @@ impl_node_port_alloc_buffers(struct spa_node *node, spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->out_ports[port_id]; + port = GET_PORT(this, direction, port_id); if (!port->have_format) return -EIO; @@ -699,6 +731,17 @@ impl_node_port_alloc_buffers(struct spa_node *node, return res; } +static struct control *find_control(struct port *port, uint32_t id) +{ + int i; + + for (i = 0; i < port->n_controls; i++) { + if (port->controls[i].id == id) + return &port->controls[i]; + } + return NULL; +} + static int impl_node_port_set_io(struct spa_node *node, enum spa_direction direction, uint32_t port_id, @@ -707,6 +750,8 @@ static int impl_node_port_set_io(struct spa_node *node, { struct impl *this; struct type *t; + struct port *port; + struct control *control; spa_return_val_if_fail(node != NULL, -EINVAL); @@ -715,8 +760,17 @@ static int impl_node_port_set_io(struct spa_node *node, spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - if (id == t->io.Buffers) - this->out_ports[port_id].io = data; + port = GET_PORT(this, direction, port_id); + + if (id == t->io.Buffers) { + port->io = data; + } + else if ((control = find_control(port, id))) { + if (data && size >= sizeof(struct spa_pod_double)) + control->io = &SPA_POD_VALUE(struct spa_pod_double, data); + else + control->io = &control->value; + } else return -ENOENT; @@ -735,7 +789,7 @@ static int impl_node_port_reuse_buffer(struct spa_node *node, spa_return_val_if_fail(port_id == 0, -EINVAL); this = SPA_CONTAINER_OF(node, struct impl, node); - port = &this->out_ports[port_id]; + port = GET_OUT_PORT(this, port_id); spa_return_val_if_fail(buffer_id < port->n_buffers, -EINVAL); @@ -760,7 +814,7 @@ static int impl_node_process_input(struct spa_node *node) static int impl_node_process_output(struct spa_node *node) { struct impl *this; - int res = SPA_STATUS_OK; + int i, res = SPA_STATUS_OK; struct spa_io_buffers *io; struct port *port; @@ -768,17 +822,36 @@ static int impl_node_process_output(struct spa_node *node) this = SPA_CONTAINER_OF(node, struct impl, node); - port = &this->out_ports[0]; + port = GET_OUT_PORT(this, 0); io = port->io; spa_return_val_if_fail(io != NULL, -EIO); if (io->status == SPA_STATUS_HAVE_BUFFER) return SPA_STATUS_HAVE_BUFFER; - if (io->buffer_id < this->out_ports[0].n_buffers) { + if (io->buffer_id < port->n_buffers) { res = spa_v4l2_buffer_recycle(this, io->buffer_id); io->buffer_id = SPA_ID_INVALID; } + for (i = 0; i < port->n_controls; i++) { + struct control *control = &port->controls[i]; + + if (control->io == NULL) + continue; + + if (control->value != *control->io) { + struct v4l2_control c; + + memset (&c, 0, sizeof (c)); + c.id = control->ctrl_id; + c.value = *control->io; + + if (ioctl(port->fd, VIDIOC_S_CTRL, &c) < 0) + spa_log_error(port->log, "VIDIOC_S_CTRL %m"); + + control->value = *control->io = c.value; + } + } return res; } @@ -839,7 +912,7 @@ static int impl_clock_get_time(struct spa_clock *clock, spa_return_val_if_fail(clock != NULL, -EINVAL); this = SPA_CONTAINER_OF(clock, struct impl, clock); - port = &this->out_ports[0]; + port = GET_OUT_PORT(this, 0); if (rate) *rate = SPA_USEC_PER_SEC; @@ -894,6 +967,7 @@ impl_init(const struct spa_handle_factory *factory, struct impl *this; uint32_t i; const char *str; + struct port *port; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); @@ -901,25 +975,27 @@ impl_init(const struct spa_handle_factory *factory, handle->get_interface = impl_get_interface; handle->clear = impl_clear, this = (struct impl *) handle; + port = GET_OUT_PORT(this, 0); + for (i = 0; i < n_support; i++) { if (strcmp(support[i].type, SPA_TYPE__TypeMap) == 0) this->map = support[i].data; else if (strcmp(support[i].type, SPA_TYPE__Log) == 0) this->log = support[i].data; else if (strcmp(support[i].type, SPA_TYPE_LOOP__MainLoop) == 0) - this->out_ports[0].main_loop = support[i].data; + port->main_loop = support[i].data; else if (strcmp(support[i].type, SPA_TYPE_LOOP__DataLoop) == 0) - this->out_ports[0].data_loop = support[i].data; + port->data_loop = support[i].data; } if (this->map == NULL) { spa_log_error(this->log, "a type-map is needed"); return -EINVAL; } - if (this->out_ports[0].main_loop == NULL) { + if (port->main_loop == NULL) { spa_log_error(this->log, "a main_loop is needed"); return -EINVAL; } - if (this->out_ports[0].data_loop == NULL) { + if (port->data_loop == NULL) { spa_log_error(this->log, "a data_loop is needed"); return -EINVAL; } @@ -930,11 +1006,10 @@ impl_init(const struct spa_handle_factory *factory, reset_props(&this->props); - this->out_ports[0].log = this->log; - 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; + port->log = this->log; + port->info.flags = SPA_PORT_INFO_FLAG_LIVE; + port->export_buf = true; + port->have_query_ext_ctrl = true; if (info && (str = spa_dict_lookup(info, "device.path"))) { strncpy(this->props.device, str, 63); diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index b888681fb..e81d99cdd 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -946,6 +946,30 @@ static int query_ext_ctrl_ioctl(struct port *port, struct v4l2_query_ext_ctrl *q return res; } +static uint32_t control_to_prop_id(struct impl *impl, uint32_t control_id) +{ + switch (control_id) { + case V4L2_CID_BRIGHTNESS: + return impl->type.prop_brightness; + case V4L2_CID_CONTRAST: + return impl->type.prop_contrast; + case V4L2_CID_SATURATION: + return impl->type.prop_saturation; + case V4L2_CID_HUE: + return impl->type.prop_hue; + case V4L2_CID_GAMMA: + return impl->type.prop_gamma; + case V4L2_CID_EXPOSURE: + return impl->type.prop_exposure; + case V4L2_CID_GAIN: + return impl->type.prop_gain; + case V4L2_CID_SHARPNESS: + return impl->type.prop_sharpness; + default: + return impl->type.prop_unknown; + } +} + static int spa_v4l2_enum_controls(struct impl *this, uint32_t *index, @@ -959,7 +983,7 @@ spa_v4l2_enum_controls(struct impl *this, struct spa_pod *param; struct spa_pod_builder b = { 0 }; char type_id[128]; - uint32_t id; + uint32_t id, prop_id, ctrl_id; uint8_t buffer[1024]; int res; const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; @@ -970,8 +994,10 @@ spa_v4l2_enum_controls(struct impl *this, next: spa_zero(queryctrl); - if (*index == 0) + if (*index == 0) { *index |= next_fl; + port->n_controls = 0; + } queryctrl.id = *index; spa_log_debug(port->log, "test control %08x", queryctrl.id); @@ -1003,19 +1029,30 @@ spa_v4l2_enum_controls(struct impl *this, if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) goto next; - spa_log_debug(port->log, "Control %s", queryctrl.name); + ctrl_id = queryctrl.id & ~next_fl; spa_pod_builder_init(&b, buffer, sizeof(buffer)); - snprintf(type_id, sizeof(type_id), SPA_TYPE_PARAM_IO_PROP_BASE"%08x", queryctrl.id & ~next_fl); + snprintf(type_id, sizeof(type_id), SPA_TYPE_PARAM_IO_PROP_BASE"%08x", ctrl_id); id = spa_type_map_get_id(this->map, type_id); + prop_id = control_to_prop_id(this, ctrl_id); + + port->controls[port->n_controls].id = id; + port->controls[port->n_controls].ctrl_id = ctrl_id; + port->controls[port->n_controls].value = queryctrl.default_value; + + spa_log_debug(port->log, "Control %s %d %d", queryctrl.name, prop_id, ctrl_id); + + port->n_controls++; + 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.id, "I", id, ":", t->param_io.size, "i", sizeof(struct spa_pod_int), + ":", t->param.propId, "I", prop_id, ":", t->param.propType, "isu", queryctrl.default_value, 3, queryctrl.minimum, queryctrl.maximum, @@ -1027,6 +1064,7 @@ spa_v4l2_enum_controls(struct impl *this, t->param_io.idPropsIn, t->param_io.Prop, ":", t->param_io.id, "I", id, ":", t->param_io.size, "i", sizeof(struct spa_pod_bool), + ":", t->param.propId, "I", prop_id, ":", t->param.propType, "b-u", queryctrl.default_value, ":", t->param.propName, "s", queryctrl.name); break; @@ -1037,14 +1075,12 @@ spa_v4l2_enum_controls(struct impl *this, spa_pod_builder_push_object(&b, t->param_io.idPropsIn, t->param_io.Prop); spa_pod_builder_add(&b, ":", t->param_io.id, "I", id, - ":", t->param_io.size, "i", sizeof(struct spa_pod_int), + ":", t->param_io.size, "i", sizeof(struct spa_pod_double), + ":", t->param.propId, "I", prop_id, ":", t->param.propName, "s", queryctrl.name, + ":", t->param.propType, "i-u", queryctrl.default_value, 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;