Merge branch 'feature/libcamera-controls' into 'master'

propagate libcamera control as pipewire properties

See merge request pipewire/pipewire!2355
This commit is contained in:
frogarian 2025-10-27 20:29:04 +00:00
commit b976d6cc8a

View file

@ -7,6 +7,7 @@
#include <array>
#include <cstddef>
#include <limits>
#include <mutex>
#include <optional>
#include <type_traits>
#include <utility>
@ -34,6 +35,7 @@
#include <spa/control/control.h>
#include <spa/pod/dynamic.h>
#include <spa/pod/filter.h>
#include <spa/pod/dynamic.h>
#include <libcamera/camera.h>
#include <libcamera/control_ids.h>
@ -121,6 +123,7 @@ struct impl {
struct spa_node node = {};
struct spa_log *log;
struct spa_loop *main_loop;
struct spa_loop *data_loop;
struct spa_system *system;
@ -161,10 +164,12 @@ struct impl {
ControlList ctrls;
ControlList initial_controls;
ControlList request_metadata;
std::mutex request_metadata_mutex;
bool active = false;
bool acquired = false;
impl(spa_log *log, spa_loop *data_loop, spa_system *system,
impl(spa_log *log, spa_loop *main_loop, spa_loop *data_loop, spa_system *system,
std::shared_ptr<CameraManager> manager, std::shared_ptr<Camera> camera,
std::unique_ptr<CameraConfiguration> config);
@ -1037,6 +1042,23 @@ int spa_libcamera_apply_controls(struct impl *impl, libcamera::ControlList&& con
);
}
void emit_node_info(struct impl *impl, bool full);
static int do_emit_node_info(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;
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
impl->params[NODE_Props].flags ^= SPA_PARAM_INFO_SERIAL;
emit_node_info(impl, true);
return 0;
}
void handle_completed_request(struct impl *impl, libcamera::Request *request)
{
const auto request_id = request->cookie();
@ -1111,6 +1133,34 @@ void handle_completed_request(struct impl *impl, libcamera::Request *request)
d->chunk->flags |= SPA_CHUNK_FLAG_CORRUPTED;
}
// update stored metadata from request metadata
{
const auto lock_guard = std::lock_guard{impl->request_metadata_mutex};
auto update = false;
for (const auto& [id, value] : request->metadata())
{
const auto prop_id = control_to_prop_id(id);
if (prop_id >= SPA_PROP_START_CUSTOM)
{
continue;
}
const auto it = std::find_if(impl->request_metadata.begin(), impl->request_metadata.end(), [id](const auto& entry)
{
return id == entry.first;
});
if ((it == impl->request_metadata.end()) || (value != it->second))
{
impl->request_metadata.set(id, value);
update = true;
}
}
if (update)
{
spa_loop_invoke(impl->main_loop, do_emit_node_info, 0, nullptr, 0, false, impl);
}
}
request->reuse(libcamera::Request::ReuseFlag::ReuseBuffers);
spa_list_append(&port->queue, &b->link);
@ -1305,7 +1355,6 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
return 0;
}
void impl::requestComplete(libcamera::Request *request)
{
struct impl *impl = this;
@ -1451,7 +1500,8 @@ int impl_node_enum_params(void *object, int seq,
{
struct impl *impl = (struct impl*)object;
struct spa_pod *param;
struct spa_pod_builder b = { 0 };
spa_auto(spa_pod_dynamic_builder) b = { 0 };
struct spa_pod_builder_state state;
uint8_t buffer[1024];
struct spa_result_node_params result;
uint32_t count = 0;
@ -1460,12 +1510,15 @@ int impl_node_enum_params(void *object, int seq,
spa_return_val_if_fail(impl != nullptr, -EINVAL);
spa_return_val_if_fail(num != 0, -EINVAL);
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
spa_pod_builder_get_state(&b.b, &state);
result.id = id;
result.next = start;
next:
result.index = result.next++;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
spa_pod_builder_reset(&b.b, &state);
switch (id) {
case SPA_PARAM_PropInfo:
@ -1481,6 +1534,39 @@ next:
case SPA_PARAM_Props:
{
switch (result.index) {
case 0: {
const auto add_property = [&b, &impl](unsigned int controlId, const libcamera::ControlValue& value) {
const auto id = control_to_prop_id(controlId);
if (id >= SPA_PROP_START_CUSTOM) {
return;
}
if (value.isArray()) {
return; // not yet supported
}
spa_pod_builder_prop(&b.b, id, 0);
control_value_to_pod(b.b, value);
};
struct spa_pod_frame f;
spa_pod_builder_push_object(&b.b, &f, SPA_TYPE_OBJECT_Props, id);
const auto metadata = [&]()
{
const auto lock_guard = std::lock_guard{impl->request_metadata_mutex};
auto metadata = impl->request_metadata;
return metadata;
}();
for (const auto& [id, value] : metadata)
{
add_property(id, value);
}
for (const auto& [id, info] : impl->camera->controls()) {
if (!metadata.contains(id->id())) {
add_property(id->id(), info.def());
}
}
param = (spa_pod*)spa_pod_builder_pop(&b.b, &f);
}
break;
default:
return 0;
}
@ -1490,14 +1576,14 @@ next:
return spa_libcamera_enum_format(impl, GET_OUT_PORT(impl, 0),
seq, start, num, filter);
case SPA_PARAM_Format:
if ((res = port_get_format(impl, GET_OUT_PORT(impl, 0), result.index, filter, &param, &b)) <= 0)
if ((res = port_get_format(impl, GET_OUT_PORT(impl, 0), result.index, filter, &param, &b.b)) <= 0)
return res;
break;
default:
return -ENOENT;
}
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
if (spa_pod_filter(&b.b, &result.param, param, filter) < 0)
goto next;
spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
@ -2144,11 +2230,12 @@ int impl_clear(struct spa_handle *handle)
return 0;
}
impl::impl(spa_log *log, spa_loop *data_loop, spa_system *system,
impl::impl(spa_log *log, spa_loop *main_loop, spa_loop *data_loop, spa_system *system,
std::shared_ptr<CameraManager> manager, std::shared_ptr<Camera> camera,
std::unique_ptr<CameraConfiguration> config)
: handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }),
log(log),
main_loop(main_loop),
data_loop(data_loop),
system(system),
out_ports{{this}},
@ -2200,9 +2287,15 @@ impl_init(const struct spa_handle_factory *factory,
spa_return_val_if_fail(handle != nullptr, -EINVAL);
auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log));
auto main_loop = static_cast<spa_loop *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop));
auto data_loop = static_cast<spa_loop *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop));
auto system = static_cast<spa_system *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System));
if (!main_loop) {
spa_log_error(log, "a main_loop is needed");
return -EINVAL;
}
if (!data_loop) {
spa_log_error(log, "a data_loop is needed");
return -EINVAL;
@ -2235,7 +2328,7 @@ impl_init(const struct spa_handle_factory *factory,
return -EINVAL;
}
new (handle) impl(log, data_loop, system,
new (handle) impl(log, main_loop, data_loop, system,
std::move(manager), std::move(camera),
std::move(config));