summaryrefslogtreecommitdiff
path: root/src/camera/ov9281.cpp
diff options
context:
space:
mode:
authorNikita Kostovsky <luntik2012@gmail.com>2025-01-12 17:57:45 +0100
committerNikita Kostovsky <luntik2012@gmail.com>2025-01-12 17:57:45 +0100
commit201d98f63131242bb8871ed0c4a3ae9ebd4ef030 (patch)
tree763a52710b3c8021c62b93535692b53de90265e5 /src/camera/ov9281.cpp
parent4782e81c32392c78025aae1acb4b2ed1d1395908 (diff)
start refactoring. non-working commit
Diffstat (limited to 'src/camera/ov9281.cpp')
-rw-r--r--src/camera/ov9281.cpp385
1 files changed, 385 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";
+ }
+}