diff --git a/spa/plugins/libcamera/libcamera-source.cpp b/spa/plugins/libcamera/libcamera-source.cpp index 3891b19f2..89f0a25ef 100644 --- a/spa/plugins/libcamera/libcamera-source.cpp +++ b/spa/plugins/libcamera/libcamera-source.cpp @@ -175,6 +175,7 @@ struct impl { struct spa_source source = {}; + ControlList ctrls; bool active = false; bool acquired = false; @@ -231,7 +232,9 @@ next: SPA_PROP_INFO_type, SPA_POD_String(impl->device_name.c_str())); break; default: - return 0; + return spa_libcamera_enum_controls(impl, + GET_OUT_PORT(impl, 0), + seq, result.index - 2, num, filter); } break; } @@ -275,22 +278,28 @@ static int impl_node_set_param(void *object, switch (id) { case SPA_PARAM_Props: { + struct spa_pod_object *obj = (struct spa_pod_object *) param; + struct spa_pod_prop *prop; + if (param == NULL) { impl->device_id.clear(); impl->device_name.clear(); return 0; } + SPA_POD_OBJECT_FOREACH(obj, prop) { + char device[128]; - char device[128]; - int res = spa_pod_parse_object(param, - SPA_TYPE_OBJECT_Props, NULL, - SPA_PROP_device, SPA_POD_OPT_Stringn(device, sizeof(device))); - - if (res < 0) - return res; - - impl->device_id = device; - + switch (prop->key) { + case SPA_PROP_device: + strncpy(device, (char *)SPA_POD_CONTENTS(struct spa_pod_string, &prop->value), + sizeof(device)-1); + impl->device_id = device; + break; + default: + spa_libcamera_set_control(impl, prop); + break; + } + } break; } default: diff --git a/spa/plugins/libcamera/libcamera-utils.cpp b/spa/plugins/libcamera/libcamera-utils.cpp index b7f9a1e66..bef2b5aaa 100644 --- a/spa/plugins/libcamera/libcamera-utils.cpp +++ b/spa/plugins/libcamera/libcamera-utils.cpp @@ -105,6 +105,8 @@ static int spa_libcamera_buffer_recycle(struct impl *impl, struct port *port, ui impl->pendingRequests.push_back(request); return 0; } else { + request->controls().merge(impl->ctrls); + impl->ctrls.clear(); if ((res = impl->camera->queueRequest(request)) < 0) { spa_log_warn(impl->log, "can't queue buffer %u: %s", buffer_id, spa_strerror(res)); @@ -461,9 +463,158 @@ spa_libcamera_enum_controls(struct impl *impl, struct port *port, int seq, uint32_t start, uint32_t num, const struct spa_pod *filter) { + const ControlInfoMap &info = impl->camera->controls(); + uint8_t buffer[1024]; + struct spa_pod_builder b = { 0 }; + struct spa_pod_frame f[2]; + struct spa_result_node_params result; + struct spa_pod *ctrl; + uint32_t count = 0, skip; + int res; + const ControlId *ctrl_id; + ControlInfo ctrl_info; + + result.id = SPA_PARAM_PropInfo; + result.next = start; + + auto it = info.begin(); + for (skip = result.next; skip; skip--) + it++; + + if (false) { +next: + it++; + } + result.index = result.next++; + if (it == info.end()) + goto enum_end; + + ctrl_id = it->first; + ctrl_info = it->second; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo); + spa_pod_builder_add(&b, + SPA_PROP_INFO_id, SPA_POD_Id(ctrl_id->id()), + SPA_PROP_INFO_description, SPA_POD_String(ctrl_id->name().c_str()), + 0); + + switch (ctrl_id->type()) { + case ControlTypeBool: + spa_pod_builder_add(&b, + SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool( + (bool)ctrl_info.def().get()), + 0); + break; + case ControlTypeFloat: + spa_pod_builder_add(&b, + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( + (float)ctrl_info.def().get(), + (float)ctrl_info.min().get(), + (float)ctrl_info.max().get()), + 0); + break; + case ControlTypeInteger32: + spa_pod_builder_add(&b, + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int( + (int32_t)ctrl_info.def().get(), + (int32_t)ctrl_info.min().get(), + (int32_t)ctrl_info.max().get()), + 0); + break; + default: + goto next; + } + + ctrl = (struct spa_pod*) spa_pod_builder_pop(&b, &f[0]); + + if (spa_pod_filter(&b, &result.param, ctrl, filter) < 0) + goto next; + + spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + + if (++count != num) + goto next; + +enum_end: + res = 0; + return res; +} + +struct val { + uint32_t type; + float f_val; + int32_t i_val; + bool b_val; + uint32_t id; +}; + +static int do_update_ctrls(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *impl = (struct impl *)user_data; + const struct val *d = (const struct val *)data; + switch (d->type) { + case ControlTypeBool: + impl->ctrls.set(d->id, d->b_val); + break; + case ControlTypeFloat: + impl->ctrls.set(d->id, d->f_val); + break; + case ControlTypeInteger32: + //impl->ctrls.set(d->id, (int32_t)d->i_val); + break; + default: + break; + } return 0; } +static int +spa_libcamera_set_control(struct impl *impl, const struct spa_pod_prop *prop) +{ + const ControlInfoMap &info = impl->camera->controls(); + const ControlId *ctrl_id; + int res; + struct val d; + + auto v = info.idmap().find(prop->key); + if (v == NULL) + return -ENOENT; + + ctrl_id = v->second; + + d.type = ctrl_id->type(); + d.id = ctrl_id->id(); + + switch (d.type) { + case ControlTypeBool: + if ((res = spa_pod_get_bool(&prop->value, &d.b_val)) < 0) + goto done; + break; + case ControlTypeFloat: + if ((res = spa_pod_get_float(&prop->value, &d.f_val)) < 0) + goto done; + break; + case ControlTypeInteger32: + if ((res = spa_pod_get_int(&prop->value, &d.i_val)) < 0) + goto done; + break; + default: + res = -EINVAL; + goto done; + } + spa_loop_invoke(impl->data_loop, do_update_ctrls, 0, &d, sizeof(d), true, impl); + res = 0; +done: + return res; +} + + static void libcamera_on_fd_events(struct spa_source *source) { struct impl *impl = (struct impl*) source->data; @@ -643,8 +794,14 @@ void impl::requestComplete(libcamera::Request *request) spa_log_debug(impl->log, "request complete"); + buffer_id = request->cookie(); + b = &port->buffers[buffer_id]; + if ((request->status() == Request::RequestCancelled)) { spa_log_debug(impl->log, "Request was cancelled"); + request->reuse(); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING); + spa_libcamera_buffer_recycle(impl, port, b->id); return; } FrameBuffer *buffer = request->findBuffer(stream); @@ -654,9 +811,7 @@ void impl::requestComplete(libcamera::Request *request) } const FrameMetadata &fmd = buffer->metadata(); - buffer_id = request->cookie(); - b = &port->buffers[buffer_id]; if (impl->clock) { impl->clock->nsec = fmd.timestamp; @@ -750,11 +905,14 @@ static int spa_libcamera_stream_off(struct impl *impl) return 0; } + impl->active = false; spa_log_info(impl->log, "stopping camera %s", impl->device_id.c_str()); impl->pendingRequests.clear(); - if ((res = impl->camera->stop()) < 0) - return res == -EACCES ? -EBUSY : res; + if ((res = impl->camera->stop()) < 0) { + spa_log_warn(impl->log, "error stopping camera %s: %s", + impl->device_id.c_str(), spa_strerror(res)); + } impl->camera->requestCompleted.disconnect(impl, &impl::requestComplete); @@ -765,7 +923,6 @@ static int spa_libcamera_stream_off(struct impl *impl) } spa_list_init(&port->queue); - impl->active = false; return 0; } diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index dfa99f726..f81a25929 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -216,9 +216,9 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_INFO_type, SPA_POD_Int(p->device_fd)); break; default: - return 0; + return spa_v4l2_enum_controls(this, seq, result.index - 3, num, filter); } - return spa_v4l2_enum_controls(this, seq, start, num, filter); + break; } case SPA_PARAM_Props: {