diff --git a/cmd/camera_stream.c b/cmd/camera_stream.c index beb516a..33d0fd3 100644 --- a/cmd/camera_stream.c +++ b/cmd/camera_stream.c @@ -60,12 +60,19 @@ option_value_t camera_formats[] = { {} }; +option_value_t camera_type[] = { + { "v4l2", CAMERA_V4L2 }, + { "libcamera", CAMERA_LIBCAMERA }, + {} +}; + option_t all_options[] = { DEFINE_OPTION_PTR(camera, path, string), DEFINE_OPTION(camera, width, uint), DEFINE_OPTION(camera, height, uint), DEFINE_OPTION_VALUES(camera, format, camera_formats), DEFINE_OPTION(camera, nbufs, uint), + DEFINE_OPTION_VALUES(camera, type, camera_type), DEFINE_OPTION(camera, fps, uint), DEFINE_OPTION_DEFAULT(camera, allow_dma, bool, "1"), DEFINE_OPTION(camera, high_res_factor, float), diff --git a/cmd/test_libcamera.c b/cmd/test_libcamera.c index 1917bc9..9d017b8 100644 --- a/cmd/test_libcamera.c +++ b/cmd/test_libcamera.c @@ -4,7 +4,7 @@ #include "device/camera/camera.h" log_options_t log_options = { - .debug = false, + .debug = true, .verbose = false }; @@ -12,12 +12,17 @@ int main(int argc, char *argv[]) { device_t *dev = NULL; - dev = device_libcamera_open("CAMERA", "/dev/video0"); + dev = device_libcamera_open("CAMERA", "/base/soc/i2c0mux/i2c@1/imx519@1a"); if (!dev) { printf("Failed to open libcamera\n"); return -1; } + if (device_open_buffer_list(dev, true, 2328, 1748, V4L2_PIX_FMT_VYUY, 0, 4, true) < 0) { + printf("Failed to open buffer list\n"); + return -1; + } + printf("Opened libcamera\n"); device_close(dev); diff --git a/device/camera/camera.c b/device/camera/camera.c index 6beb0bf..3df2720 100644 --- a/device/camera/camera.c +++ b/device/camera/camera.c @@ -40,7 +40,19 @@ camera_t *camera_open(camera_options_t *options) camera->name = "CAMERA"; camera->options = *options; - camera->camera = device_v4l2_open(camera->name, camera->options.path); + switch (camera->options.type) { + case CAMERA_V4L2: + camera->camera = device_v4l2_open(camera->name, camera->options.path); + break; + + case CAMERA_LIBCAMERA: + camera->camera = device_libcamera_open(camera->name, camera->options.path); + break; + + default: + E_LOG_ERROR(camera, "Unsupported camera type"); + } + if (!camera->camera) { goto error; } diff --git a/device/camera/camera.h b/device/camera/camera.h index 0c3c56e..e499868 100644 --- a/device/camera/camera.h +++ b/device/camera/camera.h @@ -9,10 +9,16 @@ #define CAMERA_DEVICE_CAMERA 0 #define CAMERA_OPTIONS_LENGTH 4096 +typedef enum { + CAMERA_V4L2 = 0, + CAMERA_LIBCAMERA +} camera_type_t; + typedef struct camera_options_s { char path[256]; unsigned width, height, format; unsigned nbufs, fps; + camera_type_t type; bool allow_dma; float high_res_factor; float low_res_factor; diff --git a/device/libcamera/buffer.cc b/device/libcamera/buffer.cc index 25bf2f0..41dbdf6 100644 --- a/device/libcamera/buffer.cc +++ b/device/libcamera/buffer.cc @@ -3,7 +3,44 @@ int libcamera_buffer_open(buffer_t *buf) { buf->libcamera = new buffer_libcamera_t{}; + buf->libcamera->request = buf->buf_list->dev->libcamera->camera->createRequest(); + if (!buf->libcamera->request) { + E_LOG_ERROR(buf, "Can't create request"); + } + + for (libcamera::StreamConfiguration &stream_cfg : *buf->buf_list->libcamera->configuration) { + auto stream = stream_cfg.stream(); + const std::vector> &buffers = + buf->buf_list->libcamera->allocator->buffers(stream); + auto const &buffer = buffers[buf->index]; + + if (buf->libcamera->request->addBuffer(stream, buffer.get()) < 0) { + E_LOG_ERROR(buf, "Can't set buffer for request"); + } + + for (auto const &plane : buffer->planes()) { + if (buf->start) { + E_LOG_ERROR(buf, "Too many planes open."); + } + + buf->start = mmap(NULL, plane.length, PROT_READ | PROT_WRITE, MAP_SHARED, plane.fd.get(), 0); + buf->length = plane.length; + buf->used = 0; + buf->dma_fd = plane.fd.get(); + + if (!buf->start) { + E_LOG_ERROR(buf, "Failed to mmap DMA buffer"); + } + + E_LOG_DEBUG(buf, "Mapped buffer: start=%p, length=%d, fd=%d", + buf->start, buf->length, buf->dma_fd); + } + } + return 0; + +error: + return -1; } void libcamera_buffer_close(buffer_t *buf) diff --git a/device/libcamera/buffer_list.cc b/device/libcamera/buffer_list.cc index cba7f33..8fd5763 100644 --- a/device/libcamera/buffer_list.cc +++ b/device/libcamera/buffer_list.cc @@ -2,13 +2,63 @@ int libcamera_buffer_list_open(buffer_list_t *buf_list, unsigned width, unsigned height, unsigned format, unsigned bytesperline, int nbufs) { + int got_bufs = 0; + if (!buf_list->do_capture) { E_LOG_INFO(buf_list, "Only capture mode is supported."); return -1; } + if (!buf_list->do_mmap) { + E_LOG_INFO(buf_list, "Only mmap buffers are supported."); + return -1; + } + buf_list->libcamera = new buffer_list_libcamera_t{}; - return 0; + buf_list->libcamera->configuration = buf_list->dev->libcamera->camera->generateConfiguration( + { libcamera::StreamRole::Viewfinder }); + + auto &configuration = buf_list->libcamera->configuration->at(0); + configuration.size = libcamera::Size(width, height); + configuration.pixelFormat = libcamera::PixelFormat(format); + if (bytesperline > 0) { + configuration.stride = bytesperline; + } + if (nbufs > 0) { + configuration.bufferCount = nbufs; + } + if (buf_list->libcamera->configuration->validate() == libcamera::CameraConfiguration::Invalid) { + E_LOG_ERROR(buf_list, "Camera configuration invalid"); + } + + if (buf_list->dev->libcamera->camera->configure(buf_list->libcamera->configuration.get()) < 0) { + E_LOG_ERROR(buf_list, "Failed to configure camera"); + } + + buf_list->fmt_width = configuration.size.width; + buf_list->fmt_height = configuration.size.height; + buf_list->fmt_format = configuration.pixelFormat.fourcc(); + buf_list->fmt_bytesperline = configuration.stride; + + buf_list->libcamera->allocator = std::make_shared( + buf_list->dev->libcamera->camera); + + got_bufs = configuration.bufferCount; + + for (libcamera::StreamConfiguration &stream_config : *buf_list->libcamera->configuration) { + if (buf_list->libcamera->allocator->allocate(stream_config.stream()) < 0) { + E_LOG_ERROR(buf_list, "Can't allocate buffers"); + } + + int allocated = buf_list->libcamera->allocator->buffers( + stream_config.stream()).size(); + got_bufs = std::min(got_bufs, allocated); + } + + return got_bufs; + +error: + return -1; } void libcamera_buffer_list_close(buffer_list_t *buf_list) @@ -21,5 +71,18 @@ void libcamera_buffer_list_close(buffer_list_t *buf_list) int libcamera_buffer_list_set_stream(buffer_list_t *buf_list, bool do_on) { + if (do_on) { + if (buf_list->dev->libcamera->camera->start() < 0) { + E_LOG_ERROR(buf_list, "Failed to start camera."); + } + } else { + if (buf_list->dev->libcamera->camera->stop() < 0) { + E_LOG_ERROR(buf_list, "Failed to stop camera."); + } + } + + return 0; + +error: return -1; } diff --git a/device/libcamera/device.cc b/device/libcamera/device.cc index ac0c171..5a620f0 100644 --- a/device/libcamera/device.cc +++ b/device/libcamera/device.cc @@ -4,18 +4,43 @@ int libcamera_device_open(device_t *dev) { dev->libcamera = new device_libcamera_t{}; - auto camera_manager = std::make_unique(); - int ret = camera_manager->start(); + dev->libcamera->camera_manager = std::make_shared(); + int ret = dev->libcamera->camera_manager->start(); if (ret < 0) { - return -1; + E_LOG_ERROR(dev, "Cannot start camera_manager."); } + dev->libcamera->camera = dev->libcamera->camera_manager->get(dev->path); + if (!dev->libcamera->camera) { + if (dev->libcamera->camera_manager->cameras().size()) { + for(auto const &camera : dev->libcamera->camera_manager->cameras()) { + E_LOG_INFO(dev, "Available Camera: %s", camera->id().c_str()); + } + } else { + E_LOG_INFO(dev, "No available cameras"); + } + E_LOG_ERROR(dev, "Camera `%s` was not found.", dev->path); + } + + if (dev->libcamera->camera->acquire()) { + E_LOG_ERROR(dev, "Failed to acquire `%s` camera.", dev->path); + } + + printf("camera manager started, and camera was found: %s\n", dev->libcamera->camera->id().c_str()); return 0; + +error: + return -1; } void libcamera_device_close(device_t *dev) { if (dev->libcamera) { + if (dev->libcamera->camera) { + dev->libcamera->camera->stop(); + dev->libcamera->camera->release(); + } + delete dev->libcamera; dev->libcamera = NULL; } diff --git a/device/libcamera/libcamera.hh b/device/libcamera/libcamera.hh index 2d32b78..7eda839 100644 --- a/device/libcamera/libcamera.hh +++ b/device/libcamera/libcamera.hh @@ -11,6 +11,7 @@ extern "C" { #include "device/buffer_list.h" #include "device/buffer.h" #include "opts/log.h" +#include "opts/fourcc.h" }; #include @@ -34,12 +35,17 @@ typedef struct device_s device_t; struct pollfd; typedef struct device_libcamera_s { + std::shared_ptr camera_manager; + std::shared_ptr camera; } device_libcamera_t; typedef struct buffer_list_libcamera_s { + std::shared_ptr configuration; + std::shared_ptr allocator; } buffer_list_libcamera_t; typedef struct buffer_libcamera_s { + std::shared_ptr request; } buffer_libcamera_t; int libcamera_device_open(device_t *dev); diff --git a/libcamera_camera.sh b/libcamera_camera.sh new file mode 100755 index 0000000..beec4dd --- /dev/null +++ b/libcamera_camera.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +cd "$SCRIPT_DIR" + +set -xeo pipefail +make -j$(nproc) +$GDB ./camera_stream \ + -camera-path=/base/soc/i2c0mux/i2c@1/imx519@1a \ + -camera-type=libcamera \ + -camera-format=YUYV \ + "$@"