pipewire/spa/plugins/libcamera/libcamera-device.cpp

489 lines
11 KiB
C++
Raw Normal View History

/* Spa libcamera device */
/* SPDX-FileCopyrightText: Copyright © 2020 Collabora Ltd. */
/* @author Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> */
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans <wim.taymans@gmail.com> */
/* SPDX-License-Identifier: MIT */
2020-04-20 12:26:50 +05:30
#include <cstddef>
#include <ios>
#include <locale>
#include <optional>
#include <span>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
2020-04-20 12:26:50 +05:30
#include <spa/support/plugin.h>
#include <spa/support/log.h>
#include <spa/support/loop.h>
#include <spa/utils/json.h>
2020-04-20 12:26:50 +05:30
#include <spa/utils/keys.h>
#include <spa/utils/result.h>
2020-04-20 12:26:50 +05:30
#include <spa/utils/names.h>
#include <spa/utils/string.h>
2020-04-20 12:26:50 +05:30
#include <spa/node/node.h>
#include <spa/pod/builder.h>
#include <spa/monitor/device.h>
#include <spa/monitor/utils.h>
#include <spa/param/param.h>
2020-04-20 12:26:50 +05:30
#include "libcamera.hpp"
2020-04-20 12:26:50 +05:30
#include <libcamera/camera.h>
#include <libcamera/property_ids.h>
using namespace libcamera;
2020-04-20 12:26:50 +05:30
namespace {
2020-04-20 12:26:50 +05:30
struct impl {
struct spa_handle handle;
struct spa_device device = {};
2020-04-20 12:26:50 +05:30
struct spa_log *log;
std::string device_id;
2020-04-20 12:26:50 +05:30
struct spa_hook_list hooks;
std::shared_ptr<CameraManager> manager;
std::shared_ptr<Camera> camera;
std::vector<std::pair<std::string, std::string>> properties;
impl(spa_log *log,
std::shared_ptr<CameraManager> manager,
std::shared_ptr<Camera> camera,
std::string device_id);
2020-04-20 12:26:50 +05:30
};
std::span<const int64_t> cameraDevice(const Camera& camera)
{
if (auto devices = camera.properties().get(properties::SystemDevices))
return devices.value();
return {};
}
std::string cameraModel(const Camera& camera)
{
return std::string(camera.properties().get(properties::Model).value_or(camera.id()));
}
const char *cameraLoc(const Camera& camera)
{
if (auto location = camera.properties().get(properties::Location)) {
switch (location.value()) {
case properties::CameraLocationFront:
return "front";
case properties::CameraLocationBack:
return "back";
case properties::CameraLocationExternal:
return "external";
}
}
return nullptr;
}
const char *cameraRot(const Camera& camera)
{
if (auto rotation = camera.properties().get(properties::Rotation)) {
switch (rotation.value()) {
case 90:
return "90";
case 180:
return "180";
case 270:
return "270";
default:
return "0";
}
}
return nullptr;
}
std::optional<std::string>
string_to_json(std::string_view str)
{
std::string result(str.size() + 2, '\0');
auto l = spa_json_encode_stringn(result.data(), result.size(), str.data(), str.size());
if (l > result.size()) {
result.resize(l);
l = spa_json_encode_stringn(result.data(), result.size(), str.data(), str.size());
if (l > result.size())
return {};
}
result.resize(l);
return result;
}
constexpr auto default_control_value_print = [](std::ostream& os, const auto& x) { os << x; };
template<typename T, typename Print = decltype(default_control_value_print)>
[[nodiscard]]
bool
control_value_to_string(std::ostream& os, const libcamera::ControlValue& v, Print print = {})
{
if (v.isArray()) {
os << "[ ";
bool first = true;
for (const auto& x : v.get<libcamera::Span<const T>>()) {
if (!first)
os << ", ";
print(os, x);
first = false;
}
os << " ]";
}
else {
print(os, v.get<T>());
}
return true;
}
[[nodiscard]]
bool
control_value_to_string(std::ostream& os, const libcamera::ControlValue& v,
const libcamera::ControlId *cid)
{
switch (v.type()) {
case libcamera::ControlTypeNone:
os << "null";
return true;
case libcamera::ControlTypeBool:
return control_value_to_string<bool>(os, v);
case libcamera::ControlTypeByte:
return control_value_to_string<uint8_t>(os, v);
case libcamera::ControlTypeUnsigned16:
return control_value_to_string<uint16_t>(os, v);
case libcamera::ControlTypeUnsigned32:
return control_value_to_string<uint32_t>(os, v);
case libcamera::ControlTypeInteger32:
return control_value_to_string<int32_t>(os, v, [&](std::ostream& os, int32_t x) {
if (cid) {
auto it = cid->enumerators().find(x);
if (it != cid->enumerators().end()) {
auto str = string_to_json(it->second);
if (str) {
os << *str;
return;
}
}
}
os << x;
});
case libcamera::ControlTypeInteger64:
return control_value_to_string<int64_t>(os, v);
case libcamera::ControlTypeFloat:
return control_value_to_string<float>(os, v);
case libcamera::ControlTypeString: {
auto str = string_to_json(v.get<std::string_view>());
if (!str)
return false;
os << *str;
return true;
}
}
return false;
}
[[nodiscard]]
std::vector<std::pair<std::string, std::string>>
control_list_to_dict(const libcamera::ControlList& list, std::string_view prefix)
{
static const std::locale c_locale("C");
std::vector<std::pair<std::string, std::string>> res;
const auto *id_map = list.idMap();
std::ostringstream value_s;
std::ostringstream key_s;
spa_assert(id_map);
key_s.imbue(c_locale);
value_s.imbue(c_locale);
value_s << std::boolalpha;
res.reserve(list.size());
for (const auto& [ id, value ] : list) {
auto it = id_map->find(id);
const auto *cid = it != id_map->end() ? it->second : nullptr;
if (!control_value_to_string(value_s, value, cid))
continue;
key_s << prefix << '.';
if (cid)
key_s << cid->vendor() << '.' << cid->name();
else
key_s << id;
res.emplace_back(std::move(key_s).str(), std::move(value_s).str());
}
return res;
}
int emit_info(struct impl *impl, bool full)
2020-04-20 12:26:50 +05:30
{
struct spa_dict dict;
2020-04-20 12:26:50 +05:30
struct spa_device_info info;
Camera& camera = *impl->camera;
2020-04-20 12:26:50 +05:30
info = SPA_DEVICE_INFO_INIT();
info.change_mask = SPA_DEVICE_CHANGE_MASK_PROPS;
std::vector<spa_dict_item> items;
#define ADD_ITEM(key, value) items.push_back({ key, value })
const auto path = "libcamera:" + impl->device_id;
ADD_ITEM(SPA_KEY_OBJECT_PATH, path.c_str());
2020-04-20 12:26:50 +05:30
ADD_ITEM(SPA_KEY_DEVICE_API, "libcamera");
ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Video/Device");
ADD_ITEM(SPA_KEY_API_LIBCAMERA_PATH, impl->device_id.c_str());
ADD_ITEM(KEY_VERSION_LIBRARY, libcamera_library_version());
ADD_ITEM(KEY_VERSION_HEADER, libcamera_header_version());
if (auto location = cameraLoc(camera))
ADD_ITEM(SPA_KEY_API_LIBCAMERA_LOCATION, location);
if (auto rotation = cameraRot(camera))
ADD_ITEM(SPA_KEY_API_LIBCAMERA_ROTATION, rotation);
const auto model = cameraModel(camera);
ADD_ITEM(SPA_KEY_DEVICE_PRODUCT_NAME, model.c_str());
ADD_ITEM(SPA_KEY_DEVICE_DESCRIPTION, model.c_str());
const auto name = "libcamera_device." + impl->device_id;
ADD_ITEM(SPA_KEY_DEVICE_NAME, name.c_str());
auto device_numbers = cameraDevice(camera);
std::string devids;
if (!device_numbers.empty()) {
std::ostringstream s;
/* encode device numbers into a json array */
s << "[ ";
for (const auto& devid : device_numbers)
s << devid << ' ';
s << ']';
devids = std::move(s).str();
ADD_ITEM(SPA_KEY_DEVICE_DEVIDS, devids.c_str());
}
2020-04-20 12:26:50 +05:30
#undef ADD_ITEM
for (const auto& [ k, v ] : impl->properties)
items.push_back({ k.c_str(), v.c_str() });
dict = SPA_DICT_INIT(items.data(), uint32_t(items.size()));
info.props = &dict;
2020-04-20 12:26:50 +05:30
spa_device_emit_info(&impl->hooks, &info);
2020-04-20 12:26:50 +05:30
if (true) {
2020-04-20 12:26:50 +05:30
struct spa_device_object_info oinfo;
oinfo = SPA_DEVICE_OBJECT_INFO_INIT();
oinfo.type = SPA_TYPE_INTERFACE_Node;
oinfo.factory_name = SPA_NAME_API_LIBCAMERA_SOURCE;
oinfo.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
oinfo.props = &dict;
2020-04-20 12:26:50 +05:30
spa_device_emit_object_info(&impl->hooks, 0, &oinfo);
2020-04-20 12:26:50 +05:30
}
return 0;
}
int impl_add_listener(void *object,
struct spa_hook *listener,
const struct spa_device_events *events,
void *data)
2020-04-20 12:26:50 +05:30
{
struct impl *impl = (struct impl*)object;
2020-04-20 12:26:50 +05:30
struct spa_hook_list save;
int res = 0;
spa_return_val_if_fail(impl != nullptr, -EINVAL);
spa_return_val_if_fail(events != nullptr, -EINVAL);
2020-04-20 12:26:50 +05:30
spa_hook_list_isolate(&impl->hooks, &save, listener, events, data);
2020-04-20 12:26:50 +05:30
if (events->info || events->object_info)
res = emit_info(impl, true);
2020-04-20 12:26:50 +05:30
spa_hook_list_join(&impl->hooks, &save);
2020-04-20 12:26:50 +05:30
return res;
}
int impl_sync(void *object, int seq)
2020-04-20 12:26:50 +05:30
{
struct impl *impl = (struct impl*) object;
2020-04-20 12:26:50 +05:30
spa_return_val_if_fail(impl != nullptr, -EINVAL);
2020-04-20 12:26:50 +05:30
spa_device_emit_result(&impl->hooks, seq, 0, 0, nullptr);
2020-04-20 12:26:50 +05:30
return 0;
}
int impl_enum_params(void *object, int seq,
uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter)
2020-04-20 12:26:50 +05:30
{
return -ENOTSUP;
}
int impl_set_param(void *object,
uint32_t id, uint32_t flags,
const struct spa_pod *param)
2020-04-20 12:26:50 +05:30
{
return -ENOTSUP;
}
const struct spa_device_methods impl_device = {
.version = SPA_VERSION_DEVICE_METHODS,
2020-04-20 12:26:50 +05:30
.add_listener = impl_add_listener,
.sync = impl_sync,
.enum_params = impl_enum_params,
.set_param = impl_set_param,
};
int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
2020-04-20 12:26:50 +05:30
{
2025-07-12 19:25:19 +02:00
auto *impl = reinterpret_cast<struct impl *>(handle);
2020-04-20 12:26:50 +05:30
spa_return_val_if_fail(handle != nullptr, -EINVAL);
spa_return_val_if_fail(interface != nullptr, -EINVAL);
2020-04-20 12:26:50 +05:30
if (spa_streq(type, SPA_TYPE_INTERFACE_Device))
*interface = &impl->device;
2020-04-20 12:26:50 +05:30
else
return -ENOENT;
return 0;
}
int impl_clear(struct spa_handle *handle)
2020-04-20 12:26:50 +05:30
{
std::destroy_at(reinterpret_cast<impl *>(handle));
2020-04-20 12:26:50 +05:30
return 0;
}
impl::impl(spa_log *log,
std::shared_ptr<CameraManager> manager,
std::shared_ptr<Camera> camera,
std::string device_id)
: handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }),
log(log),
device_id(std::move(device_id)),
manager(std::move(manager)),
camera(std::move(camera)),
properties(control_list_to_dict(this->camera->properties(), KEY_PROPERTY_PREFIX))
{
libcamera_log_topic_init(log);
spa_hook_list_init(&hooks);
device.iface = SPA_INTERFACE_INIT(
SPA_TYPE_INTERFACE_Device,
SPA_VERSION_DEVICE,
&impl_device, this);
}
size_t
2020-04-20 12:26:50 +05:30
impl_get_size(const struct spa_handle_factory *factory,
const struct spa_dict *params)
{
return sizeof(struct impl);
}
int
2020-04-20 12:26:50 +05:30
impl_init(const struct spa_handle_factory *factory,
struct spa_handle *handle,
const struct spa_dict *info,
const struct spa_support *support,
uint32_t n_support)
{
const char *str;
int res;
2020-04-20 12:26:50 +05:30
spa_return_val_if_fail(factory != nullptr, -EINVAL);
spa_return_val_if_fail(handle != nullptr, -EINVAL);
2020-04-20 12:26:50 +05:30
auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log));
2020-04-20 12:26:50 +05:30
auto manager = libcamera_manager_acquire(res);
if (!manager) {
spa_log_error(log, "can't start camera manager: %s", spa_strerror(res));
return res;
}
std::string device_id;
if (info && (str = spa_dict_lookup(info, SPA_KEY_API_LIBCAMERA_PATH)))
device_id = str;
auto camera = manager->get(device_id);
if (!camera) {
spa_log_error(log, "unknown camera id %s", device_id.c_str());
return -ENOENT;
}
new (handle) impl(log, std::move(manager), std::move(camera), std::move(device_id));
2020-04-20 12:26:50 +05:30
return 0;
}
const struct spa_interface_info impl_interfaces[] = {
2020-04-20 12:26:50 +05:30
{SPA_TYPE_INTERFACE_Device,},
};
int impl_enum_interface_info(const struct spa_handle_factory *factory,
const struct spa_interface_info **info,
uint32_t *index)
2020-04-20 12:26:50 +05:30
{
spa_return_val_if_fail(factory != nullptr, -EINVAL);
spa_return_val_if_fail(info != nullptr, -EINVAL);
spa_return_val_if_fail(index != nullptr, -EINVAL);
2020-04-20 12:26:50 +05:30
if (*index >= SPA_N_ELEMENTS(impl_interfaces))
return 0;
*info = &impl_interfaces[(*index)++];
return 1;
}
}
extern "C" {
2020-04-20 12:26:50 +05:30
const struct spa_handle_factory spa_libcamera_device_factory = {
SPA_VERSION_HANDLE_FACTORY,
SPA_NAME_API_LIBCAMERA_DEVICE,
nullptr,
2020-04-20 12:26:50 +05:30
impl_get_size,
impl_init,
impl_enum_interface_info,
};
}