summaryrefslogtreecommitdiff
path: root/src/camera
diff options
context:
space:
mode:
Diffstat (limited to 'src/camera')
-rw-r--r--src/camera/ov9281.cpp385
-rw-r--r--src/camera/ov9281.h68
2 files changed, 453 insertions, 0 deletions
diff --git a/src/camera/ov9281.cpp b/src/camera/ov9281.cpp
new file mode 100644
index 0000000..bdb9f89
--- /dev/null
+++ b/src/camera/ov9281.cpp
@@ -0,0 +1,385 @@
+#include "ov9281.h"
+
+#include <iostream>
+#include <thread>
+
+#include <string.h>
+#include <sys/mman.h>
+
+#include <libcamera/camera.h>
+#include <libcamera/camera_manager.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/framebuffer_allocator.h>
+#include <libcamera/request.h>
+
+#include "image.h"
+#include "macro.h"
+#include "pixels.h"
+#include "rotaryencoder.h"
+#include "typedefs.h"
+
+OV9281::OV9281(const std::shared_ptr<libcamera::Camera> &camera)
+ : INIT_FIELD(camera)
+{
+ std::cout << __func__ << ":\tid: " << m_camera->id();
+}
+
+OV9281::~OV9281()
+{
+ for (auto &[fd, mem] : m_mappedBuffers)
+ {
+ munmap(mem.first, mem.second);
+ }
+
+ m_camera->release();
+}
+
+bool OV9281::init()
+{
+ if (m_camera->acquire() != EXIT_SUCCESS)
+ {
+ std::cerr << __func__ << ": " << m_camera->id()
+ << ": cannot acquire camera." << std::endl;
+
+ return false;
+ }
+
+ m_config = m_camera->generateConfiguration({libcamera::StreamRole::Raw});
+
+ if (m_config->empty())
+ {
+ std::cerr << __func__ << ": " << m_camera->id()
+ << ": cannot generate configuration" << std::endl;
+
+ return false;
+ }
+
+ m_config->orientation = libcamera::Orientation::Rotate90;
+
+ if (m_config->empty())
+ {
+ std::cerr << __func__ << ": " << m_camera->id() << ": config is empty"
+ << std::endl;
+
+ return false;
+ }
+
+ libcamera::StreamConfiguration &streamConfig = m_config->at(0);
+
+ streamConfig.pixelFormat = OV9281::pixelFormat;
+ streamConfig.bufferCount = OV9281::bufferCount;
+
+ if (!validateConfig())
+ {
+ std::cerr << __func__ << ": " << m_camera->id()
+ << ": cannot apply default config" << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool OV9281::validateConfig()
+{
+ using namespace libcamera;
+
+ auto status = m_config->validate();
+
+ // WARNING: unsafe
+
+ switch (status)
+ {
+ case CameraConfiguration::Status::Valid: {
+ std::cout << __func__ << ": " << "config is valid" << std::endl;
+ break;
+ }
+ case CameraConfiguration::Status::Adjusted: {
+ if (m_config->empty())
+ {
+ std::cerr << __func__ << ": " << "config is adjusted, but empty"
+ << std::endl;
+ return false;
+ }
+
+ libcamera::StreamConfiguration &streamConfig = m_config->at(0);
+ std::cout << __func__
+ << ":\tpixelFormat: " << streamConfig.pixelFormat.toString()
+ << std::endl;
+ std::cout << __func__ << ":\tbufferCount: " << streamConfig.bufferCount
+ << std::endl;
+ std::cout << __func__ << ":\torientation: " << m_config->orientation
+ << std::endl;
+ break;
+ }
+ case CameraConfiguration::Status::Invalid: {
+ std::cerr << __func__ << ":\tconfig is invalid" << std::endl;
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool OV9281::applyConfig()
+{
+ // FIXME: may crassh even on success (e.g. by setting pixelFormat to "8")
+ if (m_camera->configure(m_config.get()) != EXIT_SUCCESS)
+ {
+ std::cerr << __func__ << ":\tcannot apply config" << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+void OV9281::onRequestCompleted(libcamera::Request *completed_request)
+{
+ using namespace libcamera;
+
+ if (completed_request->status() == Request::RequestCancelled)
+ {
+ std::cerr << __func__ << ":\trequest canceled" << std::endl;
+
+ return;
+ }
+
+ const auto &buffers = completed_request->buffers();
+
+ for (auto [stream, buffer] : buffers)
+ {
+ const auto &streamConfig = stream->configuration();
+ const auto &imageSize = streamConfig.size;
+ const auto &pixelFormat = streamConfig.pixelFormat;
+ const auto &stride = streamConfig.stride;
+
+ const FrameMetadata &metadata = buffer->metadata();
+
+ for (size_t i = 0; i < buffer->planes().size(); ++i)
+ {
+ const FrameBuffer::Plane &plane = buffer->planes()[i];
+ const FrameMetadata::Plane &metaplane = buffer->metadata()
+ .planes()[i];
+
+ size_t size = std::min(metaplane.bytesused, plane.length);
+ void *data = m_mappedBuffers[plane.fd.get()].first;
+
+ auto img = std::make_shared<Image>();
+
+ img->width = imageSize.width;
+ img->height = imageSize.height;
+
+ memcpy(img->data, data, size);
+ img->dataSize = size;
+ img->stride = stride;
+ img->pixelFormat = pixelFormat;
+ img->counters.measurementCounter = metadata.sequence;
+ img->counters.timestampUs = metadata.timestamp / 1000;
+ img->counters.encoderPosition = RotaryEncoder::instance()->position();
+
+ img->rotate();
+
+ auto pixels = img->pixels();
+#ifdef emit
+#undef emit
+ newPixels.emit(pixels);
+#define emit
+#endif
+ }
+ }
+
+ const libcamera::ControlList &metadata = completed_request->metadata();
+ const ControlInfoMap &control_map = m_camera->controls();
+ // const ControlIdMap & ctrlIdMap = control_map.idmap();
+
+ auto frameDurationCtrl = control_map.find(&controls::FrameDurationLimits);
+ double fps = frameDurationCtrl == control_map.end() ?
+ std::numeric_limits<double>::quiet_NaN() :
+ (1e6 / frameDurationCtrl->second.min().get<int64_t>());
+
+ auto exp = metadata.get(controls::ExposureTime);
+ auto ag = metadata.get(controls::AnalogueGain);
+ auto ae = metadata.get(controls::AeEnable);
+ // auto br= metadata.get(controls::Brightness);
+ static auto lastControls = completed_request->controls();
+
+ completed_request->reuse(Request::ReuseBuffers);
+ completed_request->controls().set(libcamera::controls::AeEnable, false);
+ completed_request->controls()
+ .set(libcamera::controls::draft ::NoiseReductionMode,
+ libcamera::controls::draft ::NoiseReductionModeEnum ::
+ NoiseReductionModeHighQuality);
+
+ completed_request->controls().set(libcamera::controls::ExposureTime,
+ m_exposureTime);
+
+ m_camera->queueRequest(completed_request);
+}
+
+std::vector<std::shared_ptr<OV9281>> OV9281::search(
+ std::unique_ptr<libcamera::CameraManager> &manager)
+{
+ std::vector<std::shared_ptr<OV9281>> result;
+
+ for (const auto &camera : manager->cameras())
+ {
+ auto ov9281 = std::shared_ptr<OV9281>(new OV9281(camera));
+
+ if (!ov9281->init())
+ {
+ continue;
+ }
+
+ result.push_back(ov9281);
+ }
+
+ return result;
+}
+
+bool OV9281::startStream()
+{
+ if (m_config->empty())
+ {
+ std::cerr << __func__ << ":\tconfig is empty" << std::endl;
+ return false;
+ }
+
+ auto &streamConfig = m_config->at(0);
+ m_allocator = std::make_unique<libcamera::FrameBufferAllocator>(m_camera);
+ auto stream = streamConfig.stream();
+ auto ret = m_allocator->allocate(stream);
+
+ // TODO: check if zero
+ if (ret < 0)
+ {
+ std::cerr << __func__ << ":\tcan't allocate buffers: " << strerror(ret)
+ << std::endl;
+ return false;
+ }
+
+ auto allocatedCount = ret;
+ std::cout << __func__ << ":\tallocated " << allocatedCount
+ << " buffers for stream" << std::endl;
+
+ const auto &buffers = m_allocator->buffers(stream);
+
+ for (const auto &buffer : buffers)
+ {
+ auto request = m_camera->createRequest();
+
+ if (!request)
+ {
+ std::cerr << __func__ << ":\tcan't create request" << std::endl;
+
+ return false;
+ }
+
+ // TODO: try multiple buffers per request and compare performance
+ ret = request->addBuffer(stream, buffer.get());
+
+ if (ret < 0)
+ {
+ std::cerr << __func__
+ << ":\tcan't set buffer for request: " << strerror(ret)
+ << std::endl;
+
+ return false;
+ }
+
+ for (const auto &plane : buffer->planes())
+ {
+ void *memory = mmap(NULL,
+ plane.length,
+ PROT_READ,
+ MAP_SHARED,
+ plane.fd.get(),
+ 0);
+ m_mappedBuffers[plane.fd.get()] = std::make_pair(memory,
+ plane.length);
+ }
+
+ std::int64_t lowerUS = 1 * 1000 * 1000 / desiredFPS;
+ std::int64_t higherUS = lowerUS;
+ std::int64_t value_pair[2] = {higherUS / 2, higherUS};
+ request->controls().set(libcamera::controls::AnalogueGain, 1.0);
+ request->controls().set(libcamera::controls::ExposureTime, 100);
+ request->controls().set(libcamera::controls::FrameDurationLimits,
+ libcamera::Span<const std::int64_t, 2>(
+ value_pair));
+
+ m_requests.push_back(std::move(request));
+ }
+
+ m_camera->requestCompleted.connect(this, &OV9281::onRequestCompleted);
+
+ // FIXME: memleak
+ std::unique_ptr<libcamera::ControlList> camcontrols{
+ new libcamera::ControlList()};
+
+ {
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(500ms);
+ }
+
+ ret = m_camera->start(camcontrols.get());
+
+ if (ret)
+ {
+ std::cerr << __func__ << ":\tfailed to start camera: " << strerror(ret)
+ << std::endl;
+
+ return false;
+ }
+
+ for (auto &request : m_requests)
+ {
+ ret = m_camera->queueRequest(request.get());
+
+ if (ret)
+ {
+ std::cerr << __func__
+ << ":\tfailed to queue request: " << strerror(ret)
+ << std::endl;
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void OV9281::printControls()
+{
+ using namespace libcamera;
+ const libcamera::ControlInfoMap &control_map = m_camera->controls();
+
+ // for (const auto & [id, info]: control_map)
+ for (const std::pair<const ControlId *, ControlInfo> &pair : control_map)
+ {
+ const ControlId *const &id = pair.first;
+ const ControlInfo &info = pair.second;
+
+ std::cout << "\tc " << id->name() << " (" << id->id()
+ << "): " << info.toString()
+ << (info.def().isNone() ?
+ "" :
+ " (dflt:" + info.def().toString() + ")");
+
+ if (!info.values().size())
+ {
+ std::cout << std::endl;
+ continue;
+ }
+
+ std::cout << " - [";
+
+ for (const auto &v : info.values())
+ {
+ std::cout << " " << v.toString();
+ }
+
+ std::cout << " ]\n";
+ }
+}
diff --git a/src/camera/ov9281.h b/src/camera/ov9281.h
new file mode 100644
index 0000000..f70db2f
--- /dev/null
+++ b/src/camera/ov9281.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <libcamera/base/signal.h>
+#include <libcamera/formats.h>
+
+namespace libcamera {
+class Camera;
+class CameraConfiguration;
+class CameraManager;
+class FrameBufferAllocator;
+class Request;
+} // namespace libcamera
+
+class Image;
+class Pixels;
+
+class OV9281
+{
+public:
+ ~OV9281();
+
+public:
+ static std::vector<std::shared_ptr<OV9281>> search(
+ std::unique_ptr<libcamera::CameraManager> &manager);
+
+ // public functions
+public:
+ bool startStream();
+ void printControls();
+
+ // signals
+public:
+ // TODO: image->pixels in separate thread
+ // TODO: respect sender/receiver threads
+ libcamera::Signal<std::shared_ptr<Pixels>> newPixels;
+
+private:
+ explicit OV9281(const std::shared_ptr<libcamera::Camera> &camera);
+
+ // private functions
+private:
+ bool init();
+ bool validateConfig();
+ bool applyConfig();
+
+ void onRequestCompleted(libcamera::Request *completed_request);
+
+ // constants
+private:
+ static inline constexpr auto pixelFormat{libcamera::formats::R16};
+ static inline constexpr unsigned int bufferCount{2};
+ static inline constexpr size_t desiredFPS{144};
+
+ // member variables
+private:
+ std::shared_ptr<libcamera::Camera> m_camera{nullptr};
+ std::unique_ptr<libcamera::CameraConfiguration> m_config{nullptr};
+ std::map<int, std::pair<void *, unsigned int>> m_mappedBuffers;
+ std::vector<std::unique_ptr<libcamera::Request>> m_requests;
+ std::unique_ptr<libcamera::FrameBufferAllocator> m_allocator{nullptr};
+
+ // TODO: set exposureTime from outside
+ int32_t m_exposureTime{1000};
+};