[libcamera-devel] [RFC PATCH 04/17] IPA: IPC: add IPADataSerializer

Paul Elder paul.elder at ideasonboard.com
Wed Aug 26 13:09:13 CEST 2020


This patch adds IPADataSerializer. It is a template class that
implements de/serialization. ipa_data_serializer.h implements
de/serialization for libcamera classes and primitives. This is
handwritten, and if there is any demand for any more stuctures to be
added, they must be added by hand.

There was some debate as to whether this should use some well-known wire
format, such as mojo or flatbuffers. As for a pre-made de/serializer,
such as flatbuffers that I used in the last RFC, we noticed that I had
to wrap the serialization anyway, so I decided I might as well just do
the whole thing and throw flatbuffers out the window. To my knowledge,
there is no serdes framework that can actually use generate serdes code
for a pre-defined class, except mojo, which we can't use :) So since I
had to implement manual serialization anyway, I could've followed a
pre-existing standard. I didn't, but this can be changed. To get this
RFC out I just did a simple wire format. I also think that the only
merit to following a standard format is for performance, since we're
going to be wrapping both sides of the IPC pipe in IPAProxy and
IPAProxyWorker, so it's not like the world is going to see the wire
format.

raspberrypi_serializer.h will be generated based on the mojo file seen
in the last patch. Other than that, it is conceptually the same as
ipa_data_serializer.h. This header will be included by the generated IPA
proxy and proxy worker so that they can de/serialize the data when then
call into IPC.

Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
---
 .../libcamera/internal/ipa_data_serializer.h  | 816 ++++++++++++++++++
 .../libcamera/ipa/raspberrypi_serializer.h    | 487 +++++++++++
 src/libcamera/ipa_data_serializer.cpp         |  29 +
 3 files changed, 1332 insertions(+)
 create mode 100644 include/libcamera/internal/ipa_data_serializer.h
 create mode 100644 include/libcamera/ipa/raspberrypi_serializer.h
 create mode 100644 src/libcamera/ipa_data_serializer.cpp

diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
new file mode 100644
index 00000000..368a398b
--- /dev/null
+++ b/include/libcamera/internal/ipa_data_serializer.h
@@ -0,0 +1,816 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_data_serializer.h - Image Processing Algorithm data serializer
+ */
+#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
+#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
+
+#include <deque>
+#include <iostream>
+#include <tuple>
+#include <vector>
+
+#include <libcamera/buffer.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/geometry.h>
+#include <libcamera/ipa/ipa_interface.h>
+
+#include "libcamera/internal/byte_stream_buffer.h"
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/log.h"
+
+#include <iomanip>
+
+template<typename T> std::ostream &operator<<(std::ostream &stream, const std::vector<T> &vec)
+{
+	stream << "{ ";
+	for (const T &v : vec)
+		stream << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(v) << ", ";
+
+	stream << " }";
+	return stream;
+}
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPADataSerializer)
+
+static void appendUint32(std::vector<uint8_t> &vec, uint32_t val)
+{
+	for (int i = 0; i < 4; i++)
+		vec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));
+}
+
+static void appendUint64(std::vector<uint8_t> &vec, uint64_t val)
+{
+	for (int i = 0; i < 8; i++)
+		vec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));
+}
+
+static uint32_t extractUint32(std::vector<uint8_t>::iterator it)
+{
+	uint32_t ret = 0;
+	for (int i = 0; i < 4; i++)
+		ret |= *(it + i) << 8*i;
+	return ret;
+}
+
+static uint64_t extractUint64(std::vector<uint8_t>::iterator it)
+{
+	uint32_t ret = 0;
+	for (int i = 0; i < 8; i++)
+		ret |= *(it + i) << 8*i;
+	return ret;
+}
+
+template<typename T>
+class IPADataSerializer
+{
+};
+
+template<typename V>
+class IPADataSerializer<std::vector<V>>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		std::vector<int32_t> fds_vec;
+
+		// serialize the length
+		uint32_t vec_len = data.size();
+		appendUint32(data_vec, vec_len);
+
+		// serialize the members
+		for (auto it = data.begin(); it != data.end(); ++it) {
+			std::vector<uint8_t> dvec;
+			std::vector<int32_t> fvec;
+
+			std::tie(dvec, fvec) =
+				IPADataSerializer<V>::serialize(*it, cs);
+
+			appendUint32(data_vec, dvec.size());
+			appendUint32(data_vec, fvec.size());
+
+			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
+			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
+		}
+
+		return {data_vec, fds_vec};
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,
+					  std::vector<uint8_t>::iterator it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		std::vector<int32_t> fds;
+		return IPADataSerializer<std::vector<V>>::deserialize(it1, it2,
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
+					  ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,
+					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
+					  std::vector<int32_t>::iterator fds_it1,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		uint32_t vec_len = extractUint32(data_it1);
+		std::vector<V> ret(vec_len);
+
+		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
+		std::vector<int32_t>::iterator fd_it = fds_it1;
+		for (uint32_t i = 0; i < vec_len; i++) {
+			uint32_t sizeof_data = extractUint32(data_it);
+			uint32_t sizeof_fds  = extractUint32(data_it + 4);
+
+			ret[i] = IPADataSerializer<V>::deserialize(data_it + 8,
+								   data_it + 8 + sizeof_data,
+								   fd_it,
+								   fd_it + sizeof_fds,
+								   cs);
+
+			data_it += 8 + sizeof_data;
+			fd_it += sizeof_fds;
+		}
+
+		return ret;
+	}
+};
+
+template<typename K, typename V>
+class IPADataSerializer<std::map<K, V>>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		std::vector<int32_t> fds_vec;
+
+		// serialize the length
+		uint32_t map_len = data.size();
+		appendUint32(data_vec, map_len);
+
+		// serialize the members
+		for (auto it = data.begin(); it != data.end(); ++it) {
+			std::vector<uint8_t> dvec;
+			std::vector<int32_t> fvec;
+
+			std::tie(dvec, fvec) =
+				IPADataSerializer<K>::serialize(it->first, cs);
+
+			appendUint32(data_vec, dvec.size());
+			appendUint32(data_vec, fvec.size());
+
+			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
+			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
+
+			std::tie(dvec, fvec) =
+				IPADataSerializer<V>::serialize(it->second, cs);
+
+			appendUint32(data_vec, dvec.size());
+			appendUint32(data_vec, fvec.size());
+
+			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
+			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
+		}
+
+		return {data_vec, fds_vec};
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,
+					  std::vector<uint8_t>::iterator it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		std::vector<int32_t> fds;
+		return IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
+					  ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,
+					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
+					  std::vector<int32_t>::iterator fds_it1,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		std::map<K, V> ret;
+
+		uint32_t map_len = extractUint32(data_it1);
+
+		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
+		std::vector<int32_t>::iterator fd_it = fds_it1;
+		for (uint32_t i = 0; i < map_len; i++) {
+			uint32_t sizeof_data = extractUint32(data_it);
+			uint32_t sizeof_fds  = extractUint32(data_it + 4);
+
+			K key = IPADataSerializer<K>::deserialize(data_it + 8,
+								  data_it + 8 + sizeof_data,
+								  fd_it,
+								  fd_it + sizeof_fds,
+								  cs);
+
+			data_it += 8 + sizeof_data;
+			fd_it += sizeof_fds;
+			sizeof_data = extractUint32(data_it);
+			sizeof_fds  = extractUint32(data_it + 4);
+
+			const V value = IPADataSerializer<V>::deserialize(data_it + 8,
+									  data_it + 8 + sizeof_data,
+									  fd_it,
+									  fd_it + sizeof_fds,
+									  cs);
+			ret.insert({key, value});
+
+			data_it += 8 + sizeof_data;
+			fd_it += sizeof_fds;
+		}
+
+		return ret;
+	}
+};
+
+// TODO implement this for all primitives
+template<>
+class IPADataSerializer<unsigned int>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const unsigned int data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		appendUint32(data_vec, data);
+
+		return {data_vec, {}};
+	}
+
+	static unsigned int deserialize(std::vector<uint8_t> &data,
+					[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<unsigned int>::deserialize(data.begin(), data.end());
+	}
+
+	static unsigned int deserialize(std::vector<uint8_t>::iterator it1,
+					[[maybe_unused]] std::vector<uint8_t>::iterator it2,
+					[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return extractUint32(it1);
+	}
+
+	static unsigned int deserialize(std::vector<uint8_t> &data,
+					[[maybe_unused]] std::vector<int32_t> &fds,
+					[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<unsigned int>::deserialize(data.begin(), data.end());
+	}
+
+	static unsigned int deserialize(std::vector<uint8_t>::iterator data_it1,
+					std::vector<uint8_t>::iterator data_it2,
+					[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+					[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<unsigned int>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<FileDescriptor>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec = { data.isValid() };
+		std::vector<int32_t> fd_vec;
+		if (data.isValid())
+			fd_vec.push_back(data.fd());
+
+		return {data_vec, fd_vec};
+	}
+
+	static FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
+					  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),
+								      fds.begin(), fds.end());
+	}
+
+	static FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,
+					  std::vector<uint8_t>::iterator data_it2,
+					  std::vector<int32_t>::iterator fds_it1,
+					  std::vector<int32_t>::iterator fds_it2,
+					  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		if (std::distance(data_it1, data_it2) < 1)
+			LOG(IPADataSerializer, Fatal)
+				<< "Invalid data to deserialize FileDescriptor";
+
+		bool valid = *data_it1;
+
+		if (valid && std::distance(fds_it1, fds_it2) < 1)
+			LOG(IPADataSerializer, Fatal)
+				<< "Invalid fds to deserialize FileDescriptor";
+
+		return valid ? FileDescriptor(*fds_it1) : FileDescriptor();
+	}
+};
+
+template<>
+class IPADataSerializer<IPASettings>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec(data.configurationFile.begin(),
+					      data.configurationFile.end());
+
+		return {data_vec, {}};
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t>::iterator it1,
+				       std::vector<uint8_t>::iterator it2,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::string str(it1, it2);
+
+		IPASettings ret;
+		ret.configurationFile = str;
+
+		return ret;
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] std::vector<int32_t> &fds,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,
+				       std::vector<uint8_t>::iterator data_it2,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<CameraSensorInfo>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+
+		uint32_t str_len = data.model.size();
+		appendUint32(data_vec, str_len);
+
+		data_vec.insert(data_vec.end(), data.model.begin(), data.model.end());
+
+		appendUint32(data_vec, data.bitsPerPixel);
+
+		appendUint32(data_vec, data.activeAreaSize.width);
+		appendUint32(data_vec, data.activeAreaSize.height);
+
+		appendUint32(data_vec, static_cast<uint32_t>(data.analogCrop.x));
+		appendUint32(data_vec, static_cast<uint32_t>(data.analogCrop.y));
+		appendUint32(data_vec, data.analogCrop.width);
+		appendUint32(data_vec, data.analogCrop.height);
+
+		appendUint32(data_vec, data.outputSize.width);
+		appendUint32(data_vec, data.outputSize.height);
+
+		appendUint64(data_vec, data.pixelRate);
+
+		appendUint32(data_vec, data.lineLength);
+
+		return {data_vec, {}};
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,
+					    [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		CameraSensorInfo ret;
+
+		uint32_t str_len = extractUint32(it1);
+		std::string str(it1 + 4, it1 + 4 + str_len);
+		ret.model = str;
+
+		std::vector<uint8_t>::iterator it = it1 + 4 + str_len;
+
+		ret.bitsPerPixel = extractUint32(it);
+
+		ret.activeAreaSize.width = extractUint32(it + 4);
+		ret.activeAreaSize.height = extractUint32(it + 8);
+
+		ret.analogCrop.x = static_cast<int32_t>(extractUint32(it + 12));
+		ret.analogCrop.y = static_cast<int32_t>(extractUint32(it + 16));
+		ret.analogCrop.width = extractUint32(it + 20);
+		ret.analogCrop.height = extractUint32(it + 24);
+
+		ret.outputSize.width = extractUint32(it + 28);
+		ret.outputSize.height = extractUint32(it + 32);
+
+		ret.pixelRate = extractUint64(it + 36);
+
+		ret.lineLength = extractUint64(it + 44);
+
+		return ret;
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
+					    [[maybe_unused]] std::vector<int32_t> &fds,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,
+					    std::vector<uint8_t>::iterator data_it2,
+					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<IPAStream>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+
+		appendUint32(data_vec, data.pixelFormat);
+
+		appendUint32(data_vec, data.size.width);
+		appendUint32(data_vec, data.size.height);
+
+		return {data_vec, {}};
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t> &data,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t>::iterator it1,
+				     [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		IPAStream ret;
+
+		ret.pixelFormat = extractUint32(it1);
+
+		ret.size.width = extractUint32(it1 + 4);
+		ret.size.height = extractUint32(it1 + 8);
+
+		return ret;
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t> &data,
+				     [[maybe_unused]] std::vector<int32_t> &fds,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,
+				     std::vector<uint8_t>::iterator data_it2,
+				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<ControlList>
+{
+public:
+	// map arg will be generated, since it's per-pipeline anyway
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const ControlList &data, const ControlInfoMap &map,
+		  ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for serialization of ControlList";
+
+		size_t size = cs->binarySize(map);
+		std::vector<uint8_t> infoData(size);
+		ByteStreamBuffer buffer(infoData.data(), infoData.size());
+		int ret = cs->serialize(map, buffer);
+
+		if (ret < 0 || buffer.overflow()) {
+			std::cerr << "Failed to serialize ControlList's ControlInfoMap" << std::endl;
+			return {{}, {}};
+		}
+
+		size = cs->binarySize(data);
+		std::vector<uint8_t> listData(size);
+		buffer = ByteStreamBuffer(listData.data(), listData.size());
+		ret = cs->serialize(data, buffer);
+
+		if (ret < 0 || buffer.overflow()) {
+			std::cerr << "Failed to serialize ControlList" << std::endl;
+			return {{}, {}};
+		}
+
+		std::vector<uint8_t> data_vec;
+		appendUint32(data_vec, infoData.size());
+		appendUint32(data_vec, listData.size());
+		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
+		data_vec.insert(data_vec.end(), listData.begin(), listData.end());
+
+		return {data_vec, {}};
+	}
+
+	static ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)
+	{
+		return IPADataSerializer<ControlList>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static ControlList deserialize(std::vector<uint8_t>::iterator it1,
+				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				       ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for deserialization of ControlList";
+
+		uint32_t infoDataSize = extractUint32(it1);
+		uint32_t listDataSize = extractUint32(it1 + 4);
+
+		std::vector<uint8_t>::iterator it = it1 + 8;
+
+		std::vector<uint8_t> infoData(it, it + infoDataSize);
+		std::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);
+
+		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
+		ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
+		if (map.empty() || buffer.overflow()) {
+			std::cerr << "Failed to deserialize ControlLists's ControlInfoMap" << std::endl;
+			return ControlList();
+		}
+
+		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());
+		ControlList list = cs->deserialize<ControlList>(buffer);
+		if (buffer.overflow())
+			std::cerr << "Failed to deserialize ControlList: buffer overflow" << std::endl;
+		if (list.empty())
+			std::cerr << "Failed to deserialize ControlList: empty list" << std::endl;
+
+		return list;
+	}
+
+	static ControlList deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] std::vector<int32_t> &fds,
+				       ControlSerializer *cs)
+	{
+		return IPADataSerializer<ControlList>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
+				       std::vector<uint8_t>::iterator data_it2,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				       ControlSerializer *cs)
+	{
+		return IPADataSerializer<ControlList>::deserialize(data_it1, data_it2, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<const ControlInfoMap>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const ControlInfoMap &map, ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for serialization of ControlInfoMap";
+
+		size_t size = cs->binarySize(map);
+		std::vector<uint8_t> infoData(size);
+		ByteStreamBuffer buffer(infoData.data(), infoData.size());
+		int ret = cs->serialize(map, buffer);
+
+		if (ret < 0 || buffer.overflow())
+			return {{}, {}};
+
+		std::vector<uint8_t> data_vec;
+		appendUint32(data_vec, infoData.size());
+		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
+
+		return {data_vec, {}};
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
+						ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
+						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
+						ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for deserialization of ControlInfoMap";
+
+		uint32_t infoDataSize = extractUint32(it1);
+
+		std::vector<uint8_t>::iterator it = it1 + 4;
+
+		std::vector<uint8_t> infoData(it, it + infoDataSize);
+
+		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
+		const ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
+
+		/*
+		ControlInfoMap::Map ctrls;
+		for (auto pair : map)
+			ctrls.emplace(controls::controls.at(pair.first->id()),
+				      pair.second);
+
+		ControlInfoMap ret = std::move(ctrls);
+		return ret;
+		*/
+
+		return map;
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
+						[[maybe_unused]] std::vector<int32_t> &fds,
+						ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
+					  std::vector<uint8_t>::iterator data_it2,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					  ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<FrameBuffer::Plane>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		std::vector<int32_t> fds_vec;
+
+		// fd
+		std::vector<uint8_t> fdBuf;
+		std::vector<int32_t> fdFds;
+		std::tie(fdBuf, fdFds) =
+			IPADataSerializer<FileDescriptor>::serialize(data.fd);
+		data_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());
+		fds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());
+
+		// length
+		appendUint32(data_vec, data.length);
+
+		return {data_vec, fds_vec};
+	}
+
+	static FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,
+					      std::vector<int32_t> &fds,
+					      ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),
+									  fds.begin(), fds.end(),
+									  cs);
+	}
+
+	static FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,
+					      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
+					      std::vector<int32_t>::iterator fds_it1,
+					      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					      [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		FrameBuffer::Plane ret;
+
+		ret.fd = IPADataSerializer<FileDescriptor>::deserialize(data_it1, data_it1 + 1,
+									fds_it1, fds_it1 + 1);
+		ret.length = extractUint32(data_it1 + 1);
+
+		return ret;
+	}
+};
+
+
+template<>
+class IPADataSerializer<IPABuffer>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const IPABuffer &data, ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+
+		appendUint32(data_vec, data.id);
+
+		std::vector<uint8_t> planes_data_vec;
+		std::vector<int32_t> planes_fds_vec;
+		std::tie(planes_data_vec, planes_fds_vec) =
+			IPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);
+
+		data_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());
+
+		return {data_vec, planes_fds_vec};
+	}
+
+	static IPABuffer deserialize(std::vector<uint8_t> &data,
+				     std::vector<int32_t> &fds,
+				     ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),
+								 fds.begin(), fds.end(), cs);
+	}
+
+	static IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,
+				     std::vector<uint8_t>::iterator data_it2,
+				     std::vector<int32_t>::iterator fds_it1,
+				     std::vector<int32_t>::iterator fds_it2,
+				     ControlSerializer *cs = nullptr)
+	{
+		IPABuffer ret;
+
+		ret.id = extractUint32(data_it1);
+
+		ret.planes =
+			IPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(
+				data_it1 + 4, data_it2, fds_it1, fds_it2, cs);
+
+		return ret;
+	}
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */
diff --git a/include/libcamera/ipa/raspberrypi_serializer.h b/include/libcamera/ipa/raspberrypi_serializer.h
new file mode 100644
index 00000000..01d69986
--- /dev/null
+++ b/include/libcamera/ipa/raspberrypi_serializer.h
@@ -0,0 +1,487 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * raspberrypi_serializer.h - Image Processing Algorithm data serializer for raspberry pi
+ */
+
+// automatically generated by custom compiler
+
+#include <libcamera/ipa/raspberrypi.h>
+#include <libcamera/ipa/raspberrypi_wrapper.h>
+
+#include "libcamera/internal/ipa_data_serializer.h"
+
+#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_RASPBERRYPI_H__
+#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_RASPBERRYPI_H__
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPADataSerializer)
+
+template<>
+class IPADataSerializer<RPiStaggeredWritePayload>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const RPiStaggeredWritePayload data,
+		  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> ret_data;
+
+		// scalar gainDelay_
+		appendUint32(ret_data, data.gainDelay_);
+
+		// scalar exposureDelay_
+		appendUint32(ret_data, data.exposureDelay_);
+
+		// scalar sensorMetadata_
+		appendUint32(ret_data, data.sensorMetadata_);
+
+		return {ret_data, {}};
+	}
+
+	static RPiStaggeredWritePayload deserialize(std::vector<uint8_t> &data,
+						    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		RPiStaggeredWritePayload ret;
+		std::vector<uint8_t>::iterator m = data.begin();
+
+		// scalar gainDelay_
+		ret.gainDelay_ = extractUint32(m);
+		m += 4;
+
+		// scalar exposureDelay_
+		ret.exposureDelay_ = extractUint32(m);
+		m += 4;
+
+		// scalar sensorMetadata_
+		ret.sensorMetadata_ = extractUint32(m);
+
+		return ret;
+	}
+
+	static RPiStaggeredWritePayload deserialize(std::vector<uint8_t>::iterator it1,
+						    std::vector<uint8_t>::iterator it2,
+						    ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data(it1, it2);
+		return IPADataSerializer<RPiStaggeredWritePayload>::deserialize(data, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<RPiIspPreparePayload>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const RPiIspPreparePayload data,
+		  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> ret_data;
+
+		// scalar embeddedbufferId_
+		appendUint32(ret_data, data.embeddedbufferId_);
+
+		// scalar bayerbufferId_
+		appendUint32(ret_data, data.bayerbufferId_);
+
+		return {ret_data, {}};
+	}
+
+	static RPiIspPreparePayload deserialize(std::vector<uint8_t> &data,
+						[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		RPiIspPreparePayload ret;
+		std::vector<uint8_t>::iterator m = data.begin();
+
+		// scalar embeddedbufferId_
+		ret.embeddedbufferId_ = extractUint32(m);
+		m += 4;
+
+		// scalar bayerbufferId_
+		ret.bayerbufferId_ = extractUint32(m);
+
+		return ret;
+	}
+
+	static RPiIspPreparePayload deserialize(std::vector<uint8_t>::iterator it1,
+						std::vector<uint8_t>::iterator it2,
+						ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data(it1, it2);
+		return IPADataSerializer<RPiIspPreparePayload>::deserialize(data, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<RPiStatsCompletePayload>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const RPiStatsCompletePayload data,
+		  ControlSerializer *cs)
+	{
+		std::vector<uint8_t> ret_data;
+
+		// scalar bufferId_
+		appendUint32(ret_data, data.bufferId_);
+
+		// ControlList controls_
+		if (data.controls_.size() > 0) {
+			std::vector<uint8_t> controls;
+			std::tie(controls, std::ignore) =
+				IPADataSerializer<ControlList>::serialize(data.controls_,
+						data.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,
+						cs);
+			appendUint32(ret_data, controls.size());
+			ret_data.insert(ret_data.end(), controls.begin(), controls.end());
+		} else {
+			appendUint32(ret_data, 0);
+		}
+
+
+		return {ret_data, {}};
+	}
+
+	static RPiStatsCompletePayload deserialize(std::vector<uint8_t> &data,
+						   ControlSerializer *cs)
+	{
+		RPiStatsCompletePayload ret;
+		std::vector<uint8_t>::iterator m = data.begin();
+
+		// scalar bufferId_
+		ret.bufferId_ = extractUint32(m);
+		m += 4;
+
+		// ControlList controls_
+		size_t controlsSize = extractUint32(m);
+		if (controlsSize > 0)
+			ret.controls_ =
+				IPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);
+
+		return ret;
+	}
+
+	static RPiStatsCompletePayload deserialize(std::vector<uint8_t>::iterator it1,
+						   std::vector<uint8_t>::iterator it2,
+						   ControlSerializer *cs)
+	{
+		std::vector<uint8_t> data(it1, it2);
+		return IPADataSerializer<RPiStatsCompletePayload>::deserialize(data, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<RPiConfigurePayload>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const RPiConfigurePayload data,
+		  ControlSerializer *cs)
+	{
+		std::vector<uint8_t> ret_data;
+		std::vector<int32_t> ret_fds;
+
+		// scalar op_
+		appendUint32(ret_data, data.op_);
+
+		// fd lsTableHandle_
+		std::vector<uint8_t> lsTableHandle;
+		std::vector<int32_t> lsTableHandleFds;
+		std::tie(lsTableHandle, lsTableHandleFds) =
+			IPADataSerializer<FileDescriptor>::serialize(data.lsTableHandle_);
+		ret_data.insert(ret_data.end(), lsTableHandle.begin(), lsTableHandle.end());
+		ret_fds.insert(ret_fds.end(), lsTableHandleFds.begin(), lsTableHandleFds.end());
+
+		// scalar lsTableHandleStatic_
+		appendUint32(ret_data, static_cast<uint32_t>(data.lsTableHandleStatic_));
+
+		// struct staggeredWriteResult_
+		std::vector<uint8_t> staggeredWriteResult;
+		std::tie(staggeredWriteResult, std::ignore) =
+			IPADataSerializer<RPiStaggeredWritePayload>::serialize(data.staggeredWriteResult_, cs);
+		appendUint32(ret_data, staggeredWriteResult.size());
+		ret_data.insert(ret_data.end(), staggeredWriteResult.begin(), staggeredWriteResult.end());
+
+		// ControlList controls_
+		if (data.controls_.size() > 0) {
+			std::vector<uint8_t> controls;
+			std::tie(controls, std::ignore) =
+				IPADataSerializer<ControlList>::serialize(data.controls_,
+					data.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,
+					cs);
+			appendUint32(ret_data, controls.size());
+			ret_data.insert(ret_data.end(), controls.begin(), controls.end());
+		} else {
+			appendUint32(ret_data, 0);
+		}
+
+		// scalar bufferFd_
+		appendUint32(ret_data, static_cast<uint32_t>(data.bufferFd_));
+
+		return {ret_data, ret_fds};
+	}
+
+	static RPiConfigurePayload deserialize(std::vector<uint8_t> &data,
+					       std::vector<int32_t> &fds,
+					       ControlSerializer *cs)
+	{
+		RPiConfigurePayload ret;
+		std::vector<uint8_t>::iterator m = data.begin();
+		std::vector<int32_t>::iterator n = fds.begin();
+
+		// scalar op_
+		ret.op_ = static_cast<RPiConfigParameters>(extractUint32(m));
+		m += 4;
+
+		// fd lsTableHandle_
+		ret.lsTableHandle_ = IPADataSerializer<FileDescriptor>::deserialize(m, m + 1, n, n + 1);
+		m += 1;
+		n += ret.lsTableHandle_.isValid() ? 1 : 0;
+
+		// scalar lsTableHandleStatic_
+		ret.lsTableHandleStatic_ = static_cast<int32_t>(extractUint32(m));
+		m += 4;
+
+		// struct staggeredWriteResult_
+		size_t staggeredWriteResultSize = extractUint32(m);
+		ret.staggeredWriteResult_ =
+			IPADataSerializer<RPiStaggeredWritePayload>::deserialize(m + 4, m + 4 + staggeredWriteResultSize, cs);
+		m += 4 + staggeredWriteResultSize;
+
+		// ControlList controls_
+		size_t controlsSize = extractUint32(m);
+		if (controlsSize > 0)
+			ret.controls_ =
+				IPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);
+		m += 4 + controlsSize;
+
+		// scalar bufferFd_
+		ret.bufferFd_ = static_cast<int32_t>(extractUint32(m));
+
+		return ret;
+	}
+
+	static RPiConfigurePayload deserialize(std::vector<uint8_t>::iterator data_it1,
+					       std::vector<uint8_t>::iterator data_it2,
+					       std::vector<int32_t>::iterator fds_it1,
+					       std::vector<int32_t>::iterator fds_it2,
+					       ControlSerializer *cs)
+	{
+		std::vector<uint8_t> data(data_it1, data_it2);
+		std::vector<int32_t> fds(fds_it1, fds_it2);
+		return IPADataSerializer<RPiConfigurePayload>::deserialize(data, fds, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<RPiConfigureParams>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const RPiConfigureParams data,
+		  ControlSerializer *cs)
+	{
+		std::vector<uint8_t> ret_data;
+		std::vector<int32_t> ret_fds;
+
+		// vector payload_
+		// only member, so don't need size
+		std::tie(ret_data, ret_fds) =
+			IPADataSerializer<std::vector<RPiConfigurePayload>>::serialize(data.payload_, cs);
+
+		return {ret_data, ret_fds};
+	}
+
+	static RPiConfigureParams deserialize(std::vector<uint8_t> &data,
+					      std::vector<int32_t> &fds,
+					      ControlSerializer *cs)
+	{
+		RPiConfigureParams ret;
+
+		// vector payload_
+		ret.payload_ =
+			IPADataSerializer<std::vector<RPiConfigurePayload>>::deserialize(data, fds, cs);
+
+		return ret;
+	}
+
+	static RPiConfigureParams deserialize(std::vector<uint8_t>::iterator data_it1,
+					      std::vector<uint8_t>::iterator data_it2,
+					      std::vector<int32_t>::iterator fds_it1,
+					      std::vector<int32_t>::iterator fds_it2,
+					      ControlSerializer *cs)
+	{
+		std::vector<uint8_t> data(data_it1, data_it2);
+		std::vector<int32_t> fds(fds_it1, fds_it2);
+		return IPADataSerializer<RPiConfigureParams>::deserialize(data, fds, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<RPiEventParams>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const RPiEventParams data,
+		  ControlSerializer *cs)
+	{
+		std::vector<uint8_t> ret_data;
+
+		// scalar ev_
+		appendUint32(ret_data, data.ev_);
+
+		// scalar bufferId_
+		appendUint32(ret_data, data.bufferId_);
+
+		// struct ispPrepare_
+		std::vector<uint8_t> ispPrepare;
+		std::tie(ispPrepare, std::ignore) =
+			IPADataSerializer<RPiIspPreparePayload>::serialize(data.ispPrepare_, cs);
+		appendUint32(ret_data, ispPrepare.size());
+		ret_data.insert(ret_data.end(), ispPrepare.begin(), ispPrepare.end());
+
+		// ControlList controls_
+		if (data.controls_.size() > 0) {
+			std::vector<uint8_t> controls;
+			std::tie(controls, std::ignore) =
+				IPADataSerializer<ControlList>::serialize(data.controls_,
+						data.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,
+						cs);
+			appendUint32(ret_data, controls.size());
+			ret_data.insert(ret_data.end(), controls.begin(), controls.end());
+		} else {
+			appendUint32(ret_data, 0);
+		}
+
+		// scalar bufferFd_
+		appendUint32(ret_data, static_cast<uint32_t>(data.bufferFd_));
+
+		return {ret_data, {}};
+	}
+
+	static RPiEventParams deserialize(std::vector<uint8_t> &data,
+					  ControlSerializer *cs)
+	{
+		RPiEventParams ret;
+		std::vector<uint8_t>::iterator m = data.begin();
+
+		// scalar ev_
+		ret.ev_ = static_cast<RPiEvents>(extractUint32(m));
+		m += 4;
+
+		// scalar bufferId_
+		ret.bufferId_ = extractUint32(m);
+		m += 4;
+
+		// struct ispPrepare_
+		size_t ispPrepareSize = extractUint32(m);
+		ret.ispPrepare_ =
+			IPADataSerializer<RPiIspPreparePayload>::deserialize(m + 4, m + 4 + ispPrepareSize, cs);
+		m += 4 + ispPrepareSize;
+
+		// ControlList controls_
+		size_t controlsSize = extractUint32(m);
+		if (controlsSize > 0)
+			ret.controls_ =
+				IPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);
+		m += 4 + controlsSize;
+
+		// scalar bufferFd_
+		ret.bufferFd_ = static_cast<int32_t>(extractUint32(m));
+
+		return ret;
+	}
+
+	static RPiEventParams deserialize(std::vector<uint8_t>::iterator it1,
+					  std::vector<uint8_t>::iterator it2,
+					  ControlSerializer *cs)
+	{
+		std::vector<uint8_t> data(it1, it2);
+		return IPADataSerializer<RPiEventParams>::deserialize(data, cs);
+	}
+
+};
+
+template<>
+class IPADataSerializer<RPiActionParams>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const RPiActionParams data,
+		  ControlSerializer *cs)
+	{
+		std::vector<uint8_t> ret_data;
+
+		// scalar op_
+		appendUint32(ret_data, data.op_);
+
+		// scalar bufferId_
+		appendUint32(ret_data, data.bufferId_);
+
+		// struct statsComplete_
+		std::vector<uint8_t> statsComplete;
+		std::tie(statsComplete, std::ignore) =
+			IPADataSerializer<RPiStatsCompletePayload>::serialize(data.statsComplete_, cs);
+		appendUint32(ret_data, statsComplete.size());
+		ret_data.insert(ret_data.end(), statsComplete.begin(), statsComplete.end());
+
+		// ControlList controls_
+		if (data.controls_.size() > 0) {
+			std::vector<uint8_t> controls;
+			std::tie(controls, std::ignore) =
+				IPADataSerializer<ControlList>::serialize(data.controls_,
+						data.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,
+						cs);
+			appendUint32(ret_data, controls.size());
+			ret_data.insert(ret_data.end(), controls.begin(), controls.end());
+		} else {
+			appendUint32(ret_data, 0);
+		}
+
+		return {ret_data, {}};
+	}
+
+	static RPiActionParams deserialize(std::vector<uint8_t> &data,
+					   ControlSerializer *cs)
+	{
+		RPiActionParams ret;
+		std::vector<uint8_t>::iterator m = data.begin();
+
+		// scalar op_
+		ret.op_ = static_cast<RPiActions>(extractUint32(m));
+		m += 4;
+
+		// scalar bufferId_
+		ret.bufferId_ = extractUint32(m);
+		m += 4;
+
+		// struct statsComplete_
+		size_t statsCompleteSize = extractUint32(m);
+		ret.statsComplete_ =
+			IPADataSerializer<RPiStatsCompletePayload>::deserialize(m + 4, m + 4 + statsCompleteSize, cs);
+		m += 4 + statsCompleteSize;
+
+		// ControlList controls_
+		size_t controlsSize = extractUint32(m);
+		if (controlsSize > 0)
+			ret.controls_ =
+				IPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);
+
+		return ret;
+	}
+
+	static RPiActionParams deserialize(std::vector<uint8_t>::iterator it1,
+					   std::vector<uint8_t>::iterator it2,
+					   ControlSerializer *cs)
+	{
+		std::vector<uint8_t> data(it1, it2);
+		return IPADataSerializer<RPiActionParams>::deserialize(data, cs);
+	}
+
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_RASPBERRYPI_H__ */
diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
new file mode 100644
index 00000000..86332abc
--- /dev/null
+++ b/src/libcamera/ipa_data_serializer.cpp
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_data_serializer.cpp - Image Processing Algorithm data serializer
+ */
+
+#include "libcamera/internal/ipa_data_serializer.h"
+
+#include "libcamera/internal/log.h"
+
+/**
+ * \file ipa_ipa_data_serializer.h
+ * \brief IPA Data Serializer
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPADataSerializer)
+
+/**
+ * \class IPADataSerializer
+ * \brief IPA Data Serializer
+ *
+ */
+
+// TODO the rest of the documentation
+
+} /* namespace libcamera */
-- 
2.27.0



More information about the libcamera-devel mailing list