#include #include #include #include #include #include #include #include #include #include #include #include #include "parameters.h" #include "camera.h" using libcamera::CameraManager; using libcamera::CameraConfiguration; using libcamera::Camera; using libcamera::StreamRoles; using libcamera::StreamRole; using libcamera::StreamConfiguration; using libcamera::Stream; using libcamera::ControlList; using libcamera::FrameBufferAllocator; using libcamera::FrameBuffer; using libcamera::Request; using libcamera::Span; namespace controls = libcamera::controls; namespace formats = libcamera::formats; char errbuf[256]; static void set_error(const char *format, ...) { va_list args; va_start(args, format); vsnprintf(errbuf, 256, format, args); } const char *camera_get_error() { return errbuf; } struct CameraPriv { parameters_t *params; camera_frame_cb frame_cb; std::unique_ptr camera_manager; std::shared_ptr camera; std::unique_ptr allocator; std::vector> requests; }; static int get_v4l2_colorspace(std::optional const &cs) { if (cs == libcamera::ColorSpace::Rec709) { return V4L2_COLORSPACE_REC709; } return V4L2_COLORSPACE_SMPTE170M; } bool camera_create(parameters_t *params, camera_frame_cb frame_cb, camera_t **cam) { std::unique_ptr camp = std::make_unique(); camp->camera_manager = std::make_unique(); int ret = camp->camera_manager->start(); if (ret != 0) { set_error("CameraManager.start() failed"); return false; } std::vector> cameras = camp->camera_manager->cameras(); auto rem = std::remove_if(cameras.begin(), cameras.end(), [](auto &cam) { return cam->id().find("/usb") != std::string::npos; }); cameras.erase(rem, cameras.end()); if (params->camera_id >= cameras.size()){ set_error("selected camera is not available"); return false; } camp->camera = camp->camera_manager->get(cameras[params->camera_id]->id()); if (camp->camera == NULL) { set_error("CameraManager.get() failed"); return false; } ret = camp->camera->acquire(); if (ret != 0) { set_error("Camera.acquire() failed"); return false; } StreamRoles stream_roles = { StreamRole::VideoRecording }; std::unique_ptr conf = camp->camera->generateConfiguration(stream_roles); if (conf == NULL) { set_error("Camera.generateConfiguration() failed"); return false; } StreamConfiguration &stream_conf = conf->at(0); stream_conf.pixelFormat = formats::YUV420; stream_conf.bufferCount = params->buffer_count; stream_conf.size.width = params->width; stream_conf.size.height = params->height; if (params->width >= 1280 || params->height >= 720) { stream_conf.colorSpace = libcamera::ColorSpace::Rec709; } else { stream_conf.colorSpace = libcamera::ColorSpace::Smpte170m; } CameraConfiguration::Status vstatus = conf->validate(); if (vstatus == CameraConfiguration::Invalid) { set_error("StreamConfiguration.validate() failed"); return false; } int res = camp->camera->configure(conf.get()); if (res != 0) { set_error("Camera.configure() failed"); return false; } Stream *stream = stream_conf.stream(); camp->allocator = std::make_unique(camp->camera); res = camp->allocator->allocate(stream); if (res < 0) { set_error("allocate() failed"); return false; } for (const std::unique_ptr &buffer : camp->allocator->buffers(stream)) { std::unique_ptr request = camp->camera->createRequest((uint64_t)camp.get()); if (request == NULL) { set_error("createRequest() failed"); return false; } int res = request->addBuffer(stream, buffer.get()); if (res != 0) { set_error("addBuffer() failed"); return false; } camp->requests.push_back(std::move(request)); } camp->params = params; camp->frame_cb = frame_cb; *cam = camp.release(); return true; } static void on_request_complete(Request *request) { if (request->status() == Request::RequestCancelled) { return; } CameraPriv *camp = (CameraPriv *)request->cookie(); FrameBuffer *buffer = request->buffers().begin()->second; int size = 0; for (const FrameBuffer::Plane &plane : buffer->planes()) { size += plane.length; } camp->frame_cb(buffer->planes()[0].fd.get(), size, buffer->metadata().timestamp / 1000); request->reuse(Request::ReuseFlag::ReuseBuffers); camp->camera->queueRequest(request); } int camera_get_stride(camera_t *cam) { CameraPriv *camp = (CameraPriv *)cam; return (*camp->camera->streams().begin())->configuration().stride; } int camera_get_colorspace(camera_t *cam) { CameraPriv *camp = (CameraPriv *)cam; return get_v4l2_colorspace((*camp->camera->streams().begin())->configuration().colorSpace); } bool camera_start(camera_t *cam) { CameraPriv *camp = (CameraPriv *)cam; ControlList ctrls = ControlList(controls::controls); int64_t frame_time = 1000000 / camp->params->fps; ctrls.set(controls::FrameDurationLimits, Span({ frame_time, frame_time })); int res = camp->camera->start(&ctrls); if (res != 0) { set_error("Camera.start() failed"); return false; } camp->camera->requestCompleted.connect(on_request_complete); for (std::unique_ptr &request : camp->requests) { int res = camp->camera->queueRequest(request.get()); if (res != 0) { set_error("Camera.queueRequest() failed"); return false; } } return true; }