[libcamera-devel] [RFC PATCH 05/17] IPA: IPC: add IPAProxyRPi and IPAProxyRPiWorker
Paul Elder
paul.elder at ideasonboard.com
Wed Aug 26 13:09:14 CEST 2020
This patch implements IPAProxyRPi and the corresponding proxy worker.
Both of these are meant to be generated based on some template proxy,
and the RPi IPA interface mojo definition.
The IPAProxyRPi implements an IPARPiInterface, and internally does a
switch on if isolation is enabled or not, and does either a thread call
into the IPARPi directly, or sends a message over IPC. The IPC mechanism
is implemented in an IPAIPC, and can be swapped out (though swapping out
isn't enabled yet, as we only have one IPC mechanism for now). For this
reason, the proxy (and worker) is responsible for calling the
de/serializer. On the other side, the proxy worker listens on the socket
(in the case of IPAIPCUnixSocket), and does the switch-case on the cmd
to call the appropriate function from the linked IPARPi.
For callbacks, since they're implemented as signals, we simply propagate
the signal emission (except when crossing the process boundary, which
has a send-receive).
Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
---
.../internal/ipa_proxy_raspberrypi.h | 117 ++++++
src/libcamera/proxy/ipa_proxy_raspberrypi.cpp | 381 ++++++++++++++++++
.../worker/ipa_proxy_raspberrypi_worker.cpp | 355 ++++++++++++++++
3 files changed, 853 insertions(+)
create mode 100644 include/libcamera/internal/ipa_proxy_raspberrypi.h
create mode 100644 src/libcamera/proxy/ipa_proxy_raspberrypi.cpp
create mode 100644 src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp
diff --git a/include/libcamera/internal/ipa_proxy_raspberrypi.h b/include/libcamera/internal/ipa_proxy_raspberrypi.h
new file mode 100644
index 00000000..bc2001ad
--- /dev/null
+++ b/include/libcamera/internal/ipa_proxy_raspberrypi.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_proxy_raspberrypi.h - Image Processing Algorithm proxy for Raspberry pi
+ */
+#ifndef __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__
+#define __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__
+
+// automatically generated by custom compiler
+
+// TODO move this to a proxy header directory
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/raspberrypi.h>
+#include <libcamera/ipa/raspberrypi_wrapper.h>
+
+#include "libcamera/internal/ipa_ipc.h"
+#include "libcamera/internal/ipa_proxy.h"
+#include "libcamera/internal/ipc_unixsocket.h"
+#include "libcamera/internal/thread.h"
+
+namespace libcamera {
+
+class IPAProxyRPi : public IPAProxy, public IPARPiInterface
+{
+public:
+ IPAProxyRPi(IPAModule *ipam, bool isolate);
+ ~IPAProxyRPi();
+
+ int init(const IPASettings &settings) override;
+ int start() override;
+ void stop() override;
+ void configure(const CameraSensorInfo &sensorInfo,
+ const std::map<unsigned int, IPAStream> &streamConfig,
+ const std::map<unsigned int, const ControlInfoMap> &entityControls,
+ const RPiConfigureParams &ipaConfig,
+ RPiConfigureParams *result) override;
+ void mapBuffers(const std::vector<IPABuffer> &buffers) override;
+ void unmapBuffers(const std::vector<unsigned int> &ids) override;
+ void processEvent(const RPiEventParams &event) override;
+
+ Signal<unsigned int, const RPiActionParams &> queueFrameAction;
+
+private:
+ void recvIPC(std::vector<uint8_t> &data, std::vector<int32_t> &fds);
+
+ int initThread(const IPASettings &settings);
+ int startThread();
+ void stopThread();
+ void configureThread(const CameraSensorInfo &sensorInfo,
+ const std::map<unsigned int, IPAStream> &streamConfig,
+ const std::map<unsigned int, const ControlInfoMap> &entityControls,
+ const RPiConfigureParams &ipaConfig,
+ RPiConfigureParams *result);
+ void mapBuffersThread(const std::vector<IPABuffer> &buffers);
+ void unmapBuffersThread(const std::vector<unsigned int> &ids);
+ void processEventThread(const RPiEventParams &event);
+ void frameActionHandlerThread(unsigned int frame, const RPiActionParams &data);
+
+ int initIPC(const IPASettings &settings);
+ int startIPC();
+ void stopIPC();
+ void configureIPC(const CameraSensorInfo &sensorInfo,
+ const std::map<unsigned int, IPAStream> &streamConfig,
+ const std::map<unsigned int, const ControlInfoMap> &entityControls,
+ const RPiConfigureParams &ipaConfig,
+ RPiConfigureParams *result);
+ void mapBuffersIPC(const std::vector<IPABuffer> &buffers);
+ void unmapBuffersIPC(const std::vector<unsigned int> &ids);
+ void processEventIPC(const RPiEventParams &event);
+ void frameActionHandlerIPC(std::vector<uint8_t> &data,
+ std::vector<int32_t> &fds);
+
+ /* Helper class to invoke processEvent() in another thread. */
+ class ThreadProxy : public Object
+ {
+ public:
+ void setIPA(IPARPiInterface *ipa)
+ {
+ ipa_ = ipa;
+ }
+
+ int start()
+ {
+ return ipa_->start();
+ }
+
+ void stop()
+ {
+ ipa_->stop();
+ }
+
+ void processEvent(const RPiEventParams &event)
+ {
+ ipa_->processEvent(event);
+ }
+
+ private:
+ IPARPiInterface *ipa_;
+ };
+
+ bool running_;
+ Thread thread_;
+ ThreadProxy proxy_;
+ std::unique_ptr<IPARPiInterface> ipa_;
+
+ const bool isolate_;
+
+ std::unique_ptr<IPAIPC> ipc_;
+
+ ControlSerializer controlSerializer_;
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__ */
diff --git a/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp b/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp
new file mode 100644
index 00000000..6526fb1d
--- /dev/null
+++ b/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp
@@ -0,0 +1,381 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_proxy_raspberrypi.cpp - Image Processing Algorithm proxy for Raspberry pi
+ */
+
+// automatically generated by custom compiler
+
+#include <vector>
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/ipa/raspberrypi.h>
+#include <libcamera/ipa/raspberrypi_wrapper.h>
+#include <libcamera/ipa/raspberrypi_serializer.h>
+
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/ipa_ipc.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/ipa_proxy.h"
+#include "libcamera/internal/ipc_unixsocket.h"
+#include "libcamera/internal/log.h"
+#include "libcamera/internal/process.h"
+#include "libcamera/internal/thread.h"
+
+#include "libcamera/internal/ipa_proxy_raspberrypi.h"
+
+// just inline this at code generation
+#define CALL_THREAD_OR_IPC(isolate, thread_func, ipc_func, ...) \
+ (isolate ? ipc_func(__VA_ARGS__) : thread_func(__VA_ARGS__))
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPAProxy)
+
+// TODO yeah we really need to move this into a header somewhere
+void appendUInt32(std::vector<uint8_t> &vec, uint32_t val)
+{
+ uint8_t arr[] = {static_cast<uint8_t>(val & 0xff),
+ static_cast<uint8_t>((val >> 8) & 0xff),
+ static_cast<uint8_t>((val >> 16) & 0xff),
+ static_cast<uint8_t>((val >> 24) & 0xff)};
+ vec.insert(vec.end(), arr, arr + 4);
+}
+
+IPAProxyRPi::IPAProxyRPi(IPAModule *ipam, bool isolate)
+ : IPAProxy(ipam), running_(false),
+ isolate_(isolate)
+{
+ LOG(IPAProxy, Debug)
+ << "initializing dummy proxy: loading IPA from "
+ << ipam->path();
+
+ if (isolate_) {
+ const std::string proxy_worker_path = resolvePath("ipa_proxy_raspberrypi");
+ if (proxy_worker_path.empty()) {
+ LOG(IPAProxy, Error)
+ << "Failed to get proxy worker path";
+ return;
+ }
+
+ IPAIPCFactory *ipcf = nullptr;
+ for (IPAIPCFactory *factory : IPAIPCFactory::factories()) {
+ if (!strcmp(factory->name().c_str(), "IPAIPCUnixSocket")) {
+ ipcf = factory;
+ break;
+ }
+ }
+
+ if (!ipcf) {
+ LOG(IPAProxy, Error) << "Failed to get IPAIPC factory";
+ return;
+ }
+
+ ipc_ = ipcf->create(ipam->path().c_str(), proxy_worker_path.c_str());
+ if (!ipc_->isValid()) {
+ LOG(IPAProxy, Error) << "Failed to create IPAIPC";
+ return;
+ }
+
+ ipc_->recvIPC.connect(this, &IPAProxyRPi::recvIPC);
+
+ valid_ = true;
+ return;
+ }
+
+ if (!ipam->load())
+ return;
+
+ IPAInterface *ipai = ipam->createInterface();
+ if (!ipai) {
+ LOG(IPAProxy, Error)
+ << "Failed to create IPA context for " << ipam->path();
+ return;
+ }
+
+ ipa_ = std::unique_ptr<IPARPiInterface>(dynamic_cast<IPARPiInterface *>(ipai));
+ proxy_.setIPA(ipa_.get());
+
+ /*
+ * Proxy the queueFrameAction signal to dispatch it in the caller's
+ * thread.
+ */
+ ipa_->queueFrameAction.connect(this, &IPAProxyRPi::frameActionHandlerThread);
+
+ valid_ = true;
+}
+
+IPAProxyRPi::~IPAProxyRPi()
+{
+ if (isolate_)
+ ipc_->sendAsync(CMD_EXIT, {}, {});
+}
+
+void IPAProxyRPi::recvIPC(std::vector<uint8_t> &data, std::vector<int32_t> &fds)
+{
+ uint32_t cmd = (data[0] & 0xff) |
+ ((data[1] & 0xff) << 8) |
+ ((data[2] & 0xff) << 16) |
+ ((data[3] & 0xff) << 24);
+
+ /* Need to skip another 4 bytes for the sequence number. */
+ std::vector<uint8_t> vec(data.begin() + 8, data.end());
+
+ switch (cmd) {
+ case CMD_QUEUEFRAMEACTION: {
+ frameActionHandlerIPC(vec, fds);
+ break;
+ }
+ }
+}
+
+int IPAProxyRPi::init(const IPASettings &settings)
+{
+ return CALL_THREAD_OR_IPC(isolate_, initThread, initIPC, settings);
+}
+
+int IPAProxyRPi::start()
+{
+ return CALL_THREAD_OR_IPC(isolate_, startThread, startIPC);
+}
+
+void IPAProxyRPi::stop()
+{
+ CALL_THREAD_OR_IPC(isolate_, stopThread, stopIPC);
+}
+
+void IPAProxyRPi::configure(const CameraSensorInfo &sensorInfo,
+ const std::map<unsigned int, IPAStream> &streamConfig,
+ const std::map<unsigned int, const ControlInfoMap> &entityControls,
+ const RPiConfigureParams &ipaConfig,
+ RPiConfigureParams *result)
+{
+ CALL_THREAD_OR_IPC(isolate_, configureThread, configureIPC,
+ sensorInfo, streamConfig, entityControls, ipaConfig, result);
+}
+
+void IPAProxyRPi::mapBuffers(const std::vector<IPABuffer> &buffers)
+{
+ CALL_THREAD_OR_IPC(isolate_, mapBuffersThread, mapBuffersIPC, buffers);
+}
+
+void IPAProxyRPi::unmapBuffers(const std::vector<unsigned int> &ids)
+{
+ CALL_THREAD_OR_IPC(isolate_, unmapBuffersThread, unmapBuffersIPC, ids);
+}
+
+void IPAProxyRPi::processEvent(const RPiEventParams &event)
+{
+ CALL_THREAD_OR_IPC(isolate_, processEventThread, processEventIPC, event);
+}
+
+/* Thread functions */
+
+int IPAProxyRPi::initThread(const IPASettings &settings)
+{
+ int ret = ipa_->init(settings);
+ if (ret)
+ return ret;
+
+ proxy_.moveToThread(&thread_);
+
+ return 0;
+}
+
+int IPAProxyRPi::startThread()
+{
+ running_ = true;
+ thread_.start();
+
+ return proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking);
+}
+
+void IPAProxyRPi::stopThread()
+{
+ if (!running_)
+ return;
+
+ running_ = false;
+
+ proxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking);
+
+ thread_.exit();
+ thread_.wait();
+}
+
+void IPAProxyRPi::configureThread(const CameraSensorInfo &sensorInfo,
+ const std::map<unsigned int, IPAStream> &streamConfig,
+ const std::map<unsigned int, const ControlInfoMap> &entityControls,
+ const RPiConfigureParams &ipaConfig,
+ RPiConfigureParams *result)
+{
+ ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig,
+ result);
+}
+
+void IPAProxyRPi::mapBuffersThread(const std::vector<IPABuffer> &buffers)
+{
+ ipa_->mapBuffers(buffers);
+}
+
+void IPAProxyRPi::unmapBuffersThread(const std::vector<unsigned int> &ids)
+{
+ ipa_->unmapBuffers(ids);
+}
+
+void IPAProxyRPi::processEventThread(const RPiEventParams &event)
+{
+ if (!running_)
+ return;
+
+ /* Dispatch the processEvent() call to the thread. */
+ proxy_.invokeMethod(&ThreadProxy::processEvent, ConnectionTypeQueued,
+ event);
+}
+
+void IPAProxyRPi::frameActionHandlerThread(unsigned int frame, const RPiActionParams &data)
+{
+ queueFrameAction.emit(frame, data);
+}
+
+/* IPC functions */
+
+int IPAProxyRPi::initIPC(const IPASettings &settings)
+{
+ std::vector<uint8_t> buf;
+ std::tie(buf, std::ignore) = IPADataSerializer<IPASettings>::serialize(settings);
+ std::vector<uint8_t> resultBuf;
+
+ int ret = ipc_->sendSync(CMD_INIT, buf, {}, &resultBuf);
+ if (ret < 0) {
+ LOG(IPAProxy, Error) << "Failed to call init";
+ return ret;
+ }
+ LOG(IPAProxy, Info) << "Done calling init";
+
+ /* Maybe we should cast this properly? */
+ return static_cast<int32_t>(resultBuf[0]);
+}
+
+int IPAProxyRPi::startIPC()
+{
+ std::vector<uint8_t> resultBuf;
+
+ int ret = ipc_->sendSync(CMD_START, {}, {}, &resultBuf);
+ if (ret < 0) {
+ LOG(IPAProxy, Error) << "Failed to call start";
+ return ret;
+ }
+ LOG(IPAProxy, Info) << "Done calling start";
+
+ /* Maybe we should cast this properly? */
+ return static_cast<int32_t>(resultBuf[0]);
+}
+
+void IPAProxyRPi::stopIPC()
+{
+ int ret = ipc_->sendSync(CMD_STOP, {}, {});
+ if (ret < 0)
+ LOG(IPAProxy, Error) << "Failed to call stop";
+ LOG(IPAProxy, Info) << "Done calling stop";
+}
+
+void IPAProxyRPi::configureIPC(const CameraSensorInfo &sensorInfo,
+ const std::map<unsigned int, IPAStream> &streamConfig,
+ const std::map<unsigned int, const ControlInfoMap> &entityControls,
+ const RPiConfigureParams &ipaConfig,
+ RPiConfigureParams *result)
+{
+ std::vector<uint8_t> sensorInfoBuf;
+ std::tie(sensorInfoBuf, std::ignore) =
+ IPADataSerializer<CameraSensorInfo>::serialize(sensorInfo);
+ std::vector<uint8_t> streamConfigBuf;
+ std::tie(streamConfigBuf, std::ignore) =
+ IPADataSerializer<std::map<unsigned int, IPAStream>>::serialize(streamConfig);
+ std::vector<uint8_t> entityControlsBuf;
+ std::tie(entityControlsBuf, std::ignore) =
+ IPADataSerializer<std::map<unsigned int, const ControlInfoMap>>::serialize(entityControls, &controlSerializer_);
+ std::vector<uint8_t> ipaConfigBuf;
+ std::vector<int32_t> ipaConfigFds;
+ std::tie(ipaConfigBuf, ipaConfigFds) =
+ IPADataSerializer<RPiConfigureParams>::serialize(ipaConfig, &controlSerializer_);
+
+ std::vector<uint8_t> resultBuf;
+ std::vector<int32_t> resultFds;
+
+ std::vector<uint8_t> input;
+ appendUInt32(input, sensorInfoBuf.size());
+ appendUInt32(input, streamConfigBuf.size());
+ appendUInt32(input, entityControlsBuf.size());
+ appendUInt32(input, ipaConfigBuf.size());
+ appendUInt32(input, ipaConfigFds.size());
+ input.insert(input.end(), sensorInfoBuf.begin(), sensorInfoBuf.end());
+ input.insert(input.end(), streamConfigBuf.begin(), streamConfigBuf.end());
+ input.insert(input.end(), entityControlsBuf.begin(), entityControlsBuf.end());
+ input.insert(input.end(), ipaConfigBuf.begin(), ipaConfigBuf.end());
+
+ std::vector<int32_t> fds;
+ fds.insert(fds.end(), ipaConfigFds.begin(), ipaConfigFds.end());
+
+ int ret = ipc_->sendSync(CMD_CONFIGURE, input, fds, &resultBuf, &resultFds);
+ if (ret < 0) {
+ LOG(IPAProxy, Error) << "Failed to call configure";
+ return;
+ }
+ LOG(IPAProxy, Info) << "Done calling configure";
+
+ *result = IPADataSerializer<RPiConfigureParams>::deserialize(resultBuf, resultFds, &controlSerializer_);
+}
+
+void IPAProxyRPi::mapBuffersIPC(const std::vector<IPABuffer> &buffers)
+{
+ std::vector<uint8_t> buffersBuf;
+ std::vector<int32_t> fdsBuf;
+ std::tie(buffersBuf, fdsBuf) =
+ IPADataSerializer<std::vector<IPABuffer>>::serialize(buffers);
+
+ int ret = ipc_->sendSync(CMD_MAPBUFFERS, buffersBuf, fdsBuf);
+ if (ret < 0)
+ LOG(IPAProxy, Error) << "Failed to call mapBuffers";
+ LOG(IPAProxy, Info) << "Done calling mapBuffers";
+}
+
+void IPAProxyRPi::unmapBuffersIPC(const std::vector<unsigned int> &ids)
+{
+ std::vector<uint8_t> idsBuf;
+ std::tie(idsBuf, std::ignore) =
+ IPADataSerializer<std::vector<unsigned int>>::serialize(ids);
+ int ret = ipc_->sendSync(CMD_UNMAPBUFFERS, idsBuf, {});
+ if (ret < 0)
+ LOG(IPAProxy, Error) << "Failed to call unmapBuffers";
+ LOG(IPAProxy, Info) << "Done calling unmapBuffers";
+}
+
+void IPAProxyRPi::processEventIPC(const RPiEventParams &event)
+{
+ std::vector<uint8_t> eventBuf;
+ std::tie(eventBuf, std::ignore) =
+ IPADataSerializer<RPiEventParams>::serialize(event, &controlSerializer_);
+ int ret = ipc_->sendAsync(CMD_PROCESSEVENT, eventBuf, {});
+ if (ret < 0)
+ LOG(IPAProxy, Error) << "Failed to call processEvent";
+ LOG(IPAProxy, Info) << "Done calling processEvent";
+}
+
+void IPAProxyRPi::frameActionHandlerIPC(std::vector<uint8_t> &data,
+ [[maybe_unused]] std::vector<int32_t> &fds)
+{
+ uint32_t frame = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ RPiActionParams params =
+ IPADataSerializer<RPiActionParams>::deserialize(data.begin() + 4, data.end(), &controlSerializer_);
+
+ queueFrameAction.emit(frame, params);
+}
+
+
+REGISTER_IPA_PROXY(IPAProxyRPi)
+
+} /* namespace libcamera */
diff --git a/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp b/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp
new file mode 100644
index 00000000..ad917756
--- /dev/null
+++ b/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp
@@ -0,0 +1,355 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_proxy_raspberrypi_worker.cpp - Image Processing Algorithm proxy worker for Raspberry pi
+ */
+
+// automatically generated by custom compiler
+
+#include <algorithm>
+#include <iostream>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+
+#include <libcamera/event_dispatcher.h>
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/raspberrypi_wrapper.h>
+#include <libcamera/ipa/raspberrypi_serializer.h>
+#include <libcamera/logging.h>
+
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/ipc_unixsocket.h"
+#include "libcamera/internal/log.h"
+#include "libcamera/internal/thread.h"
+
+using namespace libcamera;
+
+LOG_DEFINE_CATEGORY(IPAProxyRPiWorker)
+
+struct CallData {
+ IPCUnixSocket::Payload *response;
+ bool done;
+};
+
+IPARPiInterface *ipa_;
+IPCUnixSocket socket_;
+std::map<uint32_t, struct CallData> callData_;
+
+ControlSerializer controlSerializer_;
+
+bool exit_ = false;
+
+void writeHeader(IPCUnixSocket::Payload &payload, uint32_t cmd, uint32_t seq)
+{
+ uint8_t cmd_arr[] = {static_cast<uint8_t>(cmd & 0xff),
+ static_cast<uint8_t>((cmd >> 8 ) & 0xff),
+ static_cast<uint8_t>((cmd >> 16) & 0xff),
+ static_cast<uint8_t>((cmd >> 24) & 0xff)};
+ uint8_t seq_arr[] = {static_cast<uint8_t>(seq & 0xff),
+ static_cast<uint8_t>((seq >> 8 ) & 0xff),
+ static_cast<uint8_t>((seq >> 16) & 0xff),
+ static_cast<uint8_t>((seq >> 24) & 0xff)};
+ payload.data.insert(payload.data.begin(), cmd_arr, cmd_arr+4);
+ payload.data.insert(payload.data.begin() + 4, seq_arr, seq_arr+4);
+}
+
+// cmd, seq
+std::tuple<uint32_t, uint32_t> readHeader(IPCUnixSocket::Payload &payload)
+{
+ uint32_t cmd = (payload.data[0] & 0xff) |
+ ((payload.data[1] & 0xff) << 8) |
+ ((payload.data[2] & 0xff) << 16) |
+ ((payload.data[3] & 0xff) << 24);
+ uint32_t seq = (payload.data[4] & 0xff) |
+ ((payload.data[5] & 0xff) << 8) |
+ ((payload.data[6] & 0xff) << 16) |
+ ((payload.data[7] & 0xff) << 24);
+
+ return {cmd, seq};
+}
+
+void eraseHeader(IPCUnixSocket::Payload &payload)
+{
+ payload.data.erase(payload.data.begin(), payload.data.begin() + 8);
+}
+
+void writeUInt32(IPCUnixSocket::Payload &payload, uint32_t val, uint32_t pos)
+{
+ if (pos + 4 > payload.data.size())
+ payload.data.resize(pos + 4);
+
+ uint8_t arr[] = {static_cast<uint8_t>(val & 0xff),
+ static_cast<uint8_t>((val >> 8 ) & 0xff),
+ static_cast<uint8_t>((val >> 16) & 0xff),
+ static_cast<uint8_t>((val >> 24) & 0xff)};
+
+ std::copy(arr, arr + 4, payload.data.begin() + pos);
+}
+
+uint32_t readUInt32(IPCUnixSocket::Payload &payload, uint32_t pos)
+{
+ if (pos + 4 > payload.data.size())
+ return 0;
+
+ return (payload.data[pos] & 0xff) |
+ ((payload.data[pos + 1] & 0xff) << 8) |
+ ((payload.data[pos + 2] & 0xff) << 16) |
+ ((payload.data[pos + 3] & 0xff) << 24);
+}
+
+void readyRead(IPCUnixSocket *socket)
+{
+ IPCUnixSocket::Payload message, response;
+ int ret = socket->receive(&message);
+ if (ret) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "Receive message failed" << ret;
+ return;
+ }
+
+ uint32_t cmd, seq;
+ std::tie(cmd, seq) = readHeader(message);
+ eraseHeader(message);
+
+ switch (cmd) {
+ case CMD_INIT: {
+ IPASettings settings = IPADataSerializer<IPASettings>::deserialize(message.data);
+
+ int ret = ipa_->init(settings);
+ writeHeader(response, cmd, seq);
+ /* Maybe we should do this cast properly? */
+ response.data.push_back(static_cast<uint8_t>(ret));
+
+ ret = socket_.send(response);
+ if (ret < 0) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "Reply to init() failed" << ret;
+ }
+
+ LOG(IPAProxyRPiWorker, Info) << "Done replying to init()";
+ break;
+ }
+
+ case CMD_EXIT: {
+ exit_ = true;
+ break;
+ }
+
+ case CMD_START: {
+ int ret = ipa_->start();
+ writeHeader(response, cmd, seq);
+ /* Maybe we should do this cast properly? */
+ response.data.push_back(static_cast<uint8_t>(ret));
+
+ ret = socket_.send(response);
+ if (ret < 0) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "Reply to start() failed" << ret;
+ }
+
+ LOG(IPAProxyRPiWorker, Info) << "Done replying to start()";
+ break;
+ }
+
+ case CMD_STOP: {
+ ipa_->stop();
+
+ writeHeader(response, cmd, seq);
+ int ret = socket_.send(response);
+ if (ret < 0) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "Reply to stop() failed" << ret;
+ }
+
+ LOG(IPAProxyRPiWorker, Info) << "Done replying to stop()";
+ break;
+ }
+
+ case CMD_CONFIGURE: {
+ LOG(IPAProxyRPiWorker, Info) << "Starting configure()";
+
+ size_t sensorInfoSize = readUInt32(message, 0);
+ size_t streamConfigSize = readUInt32(message, 4);
+ size_t entityControlsSize = readUInt32(message, 8);
+
+ // TODO make this pattern more like the data ones
+ // - this will be fixed in the compiler
+ // remember to fix these offsets too
+ size_t ipaConfigFdsSize = readUInt32(message, 16);
+
+ size_t sensorInfoStart = 20;
+ size_t streamConfigStart = sensorInfoStart + sensorInfoSize;
+ size_t entityControlsStart = streamConfigStart + streamConfigSize;
+ size_t ipaConfigStart = entityControlsStart + entityControlsSize;
+
+ LOG(IPAProxyRPiWorker, Info) << " deserializing sensorInfo...";
+ struct CameraSensorInfo sensorInfo =
+ IPADataSerializer<struct CameraSensorInfo>::deserialize(
+ message.data.begin() + sensorInfoStart,
+ message.data.begin() + streamConfigStart);
+ LOG(IPAProxyRPiWorker, Info) << " deserializing streamConfig...";
+ std::map<unsigned int, struct IPAStream> streamConfig =
+ IPADataSerializer<std::map<unsigned int, struct IPAStream>>::deserialize(
+ message.data.begin() + streamConfigStart,
+ message.data.begin() + entityControlsStart);
+ LOG(IPAProxyRPiWorker, Info) << " deserializing entityControls...";
+ const std::map<unsigned int, const ControlInfoMap> entityControls =
+ IPADataSerializer<std::map<unsigned int, const ControlInfoMap>>::deserialize(
+ message.data.begin() + entityControlsStart,
+ message.data.begin() + ipaConfigStart,
+ &controlSerializer_);
+ LOG(IPAProxyRPiWorker, Info) << " deserializing ipaConfig...";
+ RPiConfigureParams ipaConfig =
+ IPADataSerializer<RPiConfigureParams>::deserialize(
+ message.data.begin() + ipaConfigStart,
+ message.data.end(),
+ message.fds.begin(),
+ message.fds.begin() + ipaConfigFdsSize,
+ &controlSerializer_);
+ RPiConfigureParams results;
+
+ LOG(IPAProxyRPiWorker, Info) << "Calling IPA's configure()";
+ ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, &results);
+ LOG(IPAProxyRPiWorker, Info) << "Done calling IPA's configure()";
+
+ std::vector<uint8_t> resultsSerialized;
+ std::vector<int32_t> resultsFds;
+ std::tie(resultsSerialized, resultsFds) =
+ IPADataSerializer<RPiConfigureParams>::serialize(results, &controlSerializer_);
+
+ writeHeader(response, cmd, seq);
+ response.data.insert(response.data.end(),
+ resultsSerialized.begin(), resultsSerialized.end());
+ response.fds.insert(response.fds.end(),
+ resultsFds.begin(), resultsFds.end());
+ LOG(IPAProxyRPiWorker, Info) << "Sending response to configure()";
+ int ret = socket_.send(response);
+ if (ret < 0) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "Reply to configure() failed" << ret;
+ }
+
+ LOG(IPAProxyRPiWorker, Info) << "Done replying to configure()";
+ break;
+ }
+
+ case CMD_MAPBUFFERS: {
+ std::vector<IPABuffer> ipaBuffers =
+ IPADataSerializer<std::vector<IPABuffer>>::deserialize(message.data, message.fds);
+ ipa_->mapBuffers(ipaBuffers);
+
+ writeHeader(response, cmd, seq);
+ int ret = socket_.send(response);
+ if (ret < 0) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "Reply to mapBuffers() failed" << ret;
+ }
+
+ LOG(IPAProxyRPiWorker, Info) << "Done replying to mapBuffers()";
+ break;
+ }
+
+ case CMD_UNMAPBUFFERS: {
+ std::vector<unsigned int> ids =
+ IPADataSerializer<std::vector<unsigned int>>::deserialize(message.data);
+ ipa_->unmapBuffers(ids);
+
+ writeHeader(response, cmd, seq);
+ int ret = socket_.send(response);
+ if (ret < 0) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "Reply to unmapBuffers() failed" << ret;
+ }
+
+ LOG(IPAProxyRPiWorker, Info) << "Done replying to unmapBuffers()";
+ break;
+ }
+
+ case CMD_PROCESSEVENT: {
+ RPiEventParams ev =
+ IPADataSerializer<RPiEventParams>::deserialize(message.data, &controlSerializer_);
+ ipa_->processEvent(ev);
+
+ LOG(IPAProxyRPiWorker, Info) << "Done processEvent()ing";
+ break;
+ }
+
+ }
+
+ return;
+}
+
+void queueFrameAction(unsigned int frame, const RPiActionParams &action)
+{
+ LOG(IPAProxyRPiWorker, Info) << "queueFrameAction triggered";
+ IPCUnixSocket::Payload message;
+ writeUInt32(message, frame, 0);
+ std::vector<uint8_t> actionVector;
+ std::tie(actionVector, std::ignore) =
+ IPADataSerializer<RPiActionParams>::serialize(action, &controlSerializer_);
+
+ message.data.insert(message.data.end(), actionVector.begin(), actionVector.end());
+ writeHeader(message, CMD_QUEUEFRAMEACTION, 0);
+
+ socket_.send(message);
+ LOG(IPAProxyRPiWorker, Info) << "queueFrameAction done";
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ /* Uncomment this for debugging. */
+ std::string logPath = "/tmp/libcamera.worker." +
+ std::to_string(getpid()) + ".log";
+ logSetFile(logPath.c_str());
+ //logSetTarget(LoggingTargetSyslog);
+
+ if (argc < 3) {
+ LOG(IPAProxyRPiWorker, Debug)
+ << "Tried to start worker with no args";
+ return EXIT_FAILURE;
+ }
+
+ int fd = std::stoi(argv[2]);
+ LOG(IPAProxyRPiWorker, Debug)
+ << "Starting worker for IPA module " << argv[1]
+ << " with IPC fd = " << fd;
+
+ std::unique_ptr<IPAModule> ipam = std::make_unique<IPAModule>(argv[1]);
+ if (!ipam->isValid() || !ipam->load()) {
+ LOG(IPAProxyRPiWorker, Error)
+ << "IPAModule " << argv[1] << " should be valid but isn't";
+ return EXIT_FAILURE;
+ }
+
+ if (socket_.bind(fd) < 0) {
+ LOG(IPAProxyRPiWorker, Error) << "IPC socket binding failed";
+ return EXIT_FAILURE;
+ }
+ socket_.readyRead.connect(&readyRead);
+
+ ipa_ = dynamic_cast<IPARPiInterface *>(ipam->createInterface());
+ if (!ipa_) {
+ LOG(IPAProxyRPiWorker, Error) << "Failed to create IPA context";
+ return EXIT_FAILURE;
+ }
+
+ ipa_->queueFrameAction.connect(&queueFrameAction);
+
+ LOG(IPAProxyRPiWorker, Debug) << "Proxy worker successfully started";
+
+ /* \todo upgrade listening loop */
+ EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
+ while (!exit_)
+ dispatcher->processEvents();
+
+ delete ipa_;
+ socket_.close();
+
+ return 0;
+}
--
2.27.0
More information about the libcamera-devel
mailing list