libcamera: handle canceled requests

When a request is canceled, recycle it so we don't run out of buffers.
Implement getting and setting controls.
This commit is contained in:
Wim Taymans 2022-10-31 19:01:39 +01:00
parent 71d58e6445
commit ef4b9745b2
3 changed files with 184 additions and 18 deletions

View file

@ -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:

View file

@ -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<bool>()),
0);
break;
case ControlTypeFloat:
spa_pod_builder_add(&b,
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
(float)ctrl_info.def().get<float>(),
(float)ctrl_info.min().get<float>(),
(float)ctrl_info.max().get<float>()),
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>(),
(int32_t)ctrl_info.min().get<int32_t>(),
(int32_t)ctrl_info.max().get<int32_t>()),
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;
}

View file

@ -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:
{