/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019, Google Inc. * * ipa_interface_wrapper.cpp - Image Processing Algorithm interface wrapper */ #include "ipa_interface_wrapper.h" #include <map> #include <string.h> #include <unistd.h> #include <vector> #include <libcamera/ipa/ipa_interface.h> #include "libcamera/internal/byte_stream_buffer.h" #include "libcamera/internal/camera_sensor.h" /** * \file ipa_interface_wrapper.h * \brief Image Processing Algorithm interface wrapper */ namespace libcamera { /** * \class IPAInterfaceWrapper * \brief Wrap an IPAInterface and expose it as an ipa_context * * This class implements the ipa_context API based on a provided IPAInterface. * It helps IPAs that implement the IPAInterface API to provide the external * ipa_context API. * * To use the wrapper, an IPA module simple creates a new instance of its * IPAInterface implementation, and passes it to the constructor of the * IPAInterfaceWrapper. As IPAInterfaceWrapper inherits from ipa_context, the * constructed wrapper can then be directly returned from the IPA module's * ipaCreate() function. * * \code{.cpp} * class MyIPA : public IPAInterface * { * ... * }; * * struct ipa_context *ipaCreate() * { * return new IPAInterfaceWrapper(std::make_unique<MyIPA>()); * } * \endcode * * The wrapper takes ownership of the IPAInterface and will automatically * delete it when the wrapper is destroyed. */ /** * \brief Construct an IPAInterfaceWrapper wrapping \a interface * \param[in] interface The interface to wrap */ IPAInterfaceWrapper::IPAInterfaceWrapper(std::unique_ptr<IPAInterface> interface) : ipa_(std::move(interface)), callbacks_(nullptr), cb_ctx_(nullptr) { ops = &operations_; ipa_->queueFrameAction.connect(this, &IPAInterfaceWrapper::queueFrameAction); } void IPAInterfaceWrapper::destroy(struct ipa_context *_ctx) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); delete ctx; } void *IPAInterfaceWrapper::get_interface(struct ipa_context *_ctx) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); return ctx->ipa_.get(); } void IPAInterfaceWrapper::init(struct ipa_context *_ctx, const struct ipa_settings *settings) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); IPASettings ipaSettings{ .configurationFile = settings->configuration_file }; ctx->ipa_->init(ipaSettings); } int IPAInterfaceWrapper::start(struct ipa_context *_ctx) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); return ctx->ipa_->start(); } void IPAInterfaceWrapper::stop(struct ipa_context *_ctx) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); ctx->ipa_->stop(); } void IPAInterfaceWrapper::register_callbacks(struct ipa_context *_ctx, const struct ipa_callback_ops *callbacks, void *cb_ctx) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); ctx->callbacks_ = callbacks; ctx->cb_ctx_ = cb_ctx; } void IPAInterfaceWrapper::configure(struct ipa_context *_ctx, const struct ipa_sensor_info *sensor_info, const struct ipa_stream *streams, unsigned int num_streams, const struct ipa_control_info_map *maps, unsigned int num_maps) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); ctx->serializer_.reset(); /* Translate the IPA sensor info. */ CameraSensorInfo sensorInfo{}; sensorInfo.model = sensor_info->model; sensorInfo.bitsPerPixel = sensor_info->bits_per_pixel; sensorInfo.activeAreaSize = { sensor_info->active_area.width, sensor_info->active_area.height }; sensorInfo.analogCrop = { sensor_info->analog_crop.left, sensor_info->analog_crop.top, sensor_info->analog_crop.width, sensor_info->analog_crop.height }; sensorInfo.outputSize = { sensor_info->output_size.width, sensor_info->output_size.height }; sensorInfo.pixelRate = sensor_info->pixel_rate; sensorInfo.lineLength = sensor_info->line_length; /* Translate the IPA stream configurations map. */ std::map<unsigned int, IPAStream> ipaStreams; for (unsigned int i = 0; i < num_streams; ++i) { const struct ipa_stream &stream = streams[i]; ipaStreams[stream.id] = { stream.pixel_format, Size(stream.width, stream.height), }; } /* Translate the IPA entity controls map. */ std::map<unsigned int, const ControlInfoMap &> entityControls; std::map<unsigned int, ControlInfoMap> infoMaps; for (unsigned int i = 0; i < num_maps; ++i) { const struct ipa_control_info_map &ipa_map = maps[i]; ByteStreamBuffer byteStream(ipa_map.data, ipa_map.size); unsigned int id = ipa_map.id; infoMaps[id] = ctx->serializer_.deserialize<ControlInfoMap>(byteStream); entityControls.emplace(id, infoMaps[id]); } /* \todo Translate the ipaConfig and result. */ IPAOperationData ipaConfig; ctx->ipa_->configure(sensorInfo, ipaStreams, entityControls, ipaConfig, nullptr); } void IPAInterfaceWrapper::map_buffers(struct ipa_context *_ctx, const struct ipa_buffer *_buffers, size_t num_buffers) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); std::vector<IPABuffer> buffers(num_buffers); for (unsigned int i = 0; i < num_buffers; ++i) { const struct ipa_buffer &_buffer = _buffers[i]; IPABuffer &buffer = buffers[i]; std::vector<FrameBuffer::Plane> &planes = buffer.planes; buffer.id = _buffer.id; planes.resize(_buffer.num_planes); for (unsigned int j = 0; j < _buffer.num_planes; ++j) { planes[j].fd = FileDescriptor(_buffer.planes[j].dmabuf); planes[j].length = _buffer.planes[j].length; } } ctx->ipa_->mapBuffers(buffers); } void IPAInterfaceWrapper::unmap_buffers(struct ipa_context *_ctx, const unsigned int *_ids, size_t num_buffers) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); std::vector<unsigned int> ids(_ids, _ids + num_buffers); ctx->ipa_->unmapBuffers(ids); } void IPAInterfaceWrapper::process_event(struct ipa_context *_ctx, const struct ipa_operation_data *data) { IPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx); IPAOperationData opData; opData.operation = data->operation; opData.data.resize(data->num_data); memcpy(opData.data.data(), data->data, data->num_data * sizeof(*data->data)); opData.controls.resize(data->num_lists); for (unsigned int i = 0; i < data->num_lists; ++i) { const struct ipa_control_list *c_list = &data->lists[i]; ByteStreamBuffer byteStream(c_list->data, c_list->size); opData.controls[i] = ctx->serializer_.deserialize<ControlList>(byteStream); } ctx->ipa_->processEvent(opData); } void IPAInterfaceWrapper::queueFrameAction(unsigned int frame, const IPAOperationData &data) { if (!callbacks_) return; struct ipa_operation_data c_data; c_data.operation = data.operation; c_data.data = data.data.data(); c_data.num_data = data.data.size(); struct ipa_control_list control_lists[data.controls.size()]; c_data.lists = control_lists; c_data.num_lists = data.controls.size(); std::size_t listsSize = 0; for (const auto &list : data.controls) listsSize += serializer_.binarySize(list); std::vector<uint8_t> binaryData(listsSize); ByteStreamBuffer byteStreamBuffer(binaryData.data(), listsSize); unsigned int i = 0; for (const auto &list : data.controls) { struct ipa_control_list &c_list = control_lists[i]; c_list.size = serializer_.binarySize(list); ByteStreamBuffer b = byteStreamBuffer.carveOut(c_list.size); serializer_.serialize(list, b); c_list.data = b.base(); } callbacks_->queue_frame_action(cb_ctx_, frame, c_data); } #ifndef __DOXYGEN__ /* * This construct confuses Doygen and makes it believe that all members of the * operations is a member of IPAInterfaceWrapper. It must thus be hidden. */ const struct ipa_context_ops IPAInterfaceWrapper::operations_ = { .destroy = &IPAInterfaceWrapper::destroy, .get_interface = &IPAInterfaceWrapper::get_interface, .init = &IPAInterfaceWrapper::init, .start = &IPAInterfaceWrapper::start, .stop = &IPAInterfaceWrapper::stop, .register_callbacks = &IPAInterfaceWrapper::register_callbacks, .configure = &IPAInterfaceWrapper::configure, .map_buffers = &IPAInterfaceWrapper::map_buffers, .unmap_buffers = &IPAInterfaceWrapper::unmap_buffers, .process_event = &IPAInterfaceWrapper::process_event, }; #endif } /* namespace libcamera */