├── CMakeLists.txt ├── LibCamera.cpp ├── LibCamera.h ├── README.md └── main.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | set(CMAKE_CXX_STANDARD 17) 3 | 4 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" ${CMAKE_MODULE_PATH}) 5 | project(libcamera-demo) 6 | find_package(PkgConfig REQUIRED) 7 | pkg_check_modules(CAMERA REQUIRED libcamera) 8 | 9 | find_library(LIBCAMERA_LIBRARY libcamera.so REQUIRED) 10 | find_library(LIBCAMERA_BASE_LIBRARY libcamera-base.so REQUIRED) 11 | message(STATUS ${CAMERA_INCLUDE_DIRS}) 12 | message(STATUS ${LIBCAMERA_LIBRARY}) 13 | message(STATUS ${LIBCAMERA_BASE_LIBRARY}) 14 | 15 | find_package(OpenCV REQUIRED) 16 | if (OpenCV_FOUND) 17 | message("Found OpenCV") 18 | message("Includes:" ${OpenCV_INCLUDE_DIRS}) 19 | endif(OpenCV_FOUND) 20 | 21 | include_directories(. "${CAMERA_INCLUDE_DIRS}") 22 | set(LIBCAMERA_LIBRARIES "${LIBCAMERA_LIBRARY}" "${LIBCAMERA_BASE_LIBRARY}") 23 | add_executable(libcamera-demo main.cpp LibCamera.cpp) 24 | 25 | target_link_libraries(libcamera-demo "${LIBCAMERA_LIBRARIES}" ${OpenCV_LIBS}) 26 | -------------------------------------------------------------------------------- /LibCamera.cpp: -------------------------------------------------------------------------------- 1 | #include "LibCamera.h" 2 | 3 | using namespace std::placeholders; 4 | 5 | int LibCamera::initCamera() { 6 | int ret; 7 | cm = std::make_unique(); 8 | ret = cm->start(); 9 | if (ret){ 10 | std::cout << "Failed to start camera manager: " 11 | << ret << std::endl; 12 | return ret; 13 | } 14 | cameraId = cm->cameras()[0]->id(); 15 | camera_ = cm->get(cameraId); 16 | if (!camera_) { 17 | std::cerr << "Camera " << cameraId << " not found" << std::endl; 18 | return 1; 19 | } 20 | 21 | if (camera_->acquire()) { 22 | std::cerr << "Failed to acquire camera " << cameraId 23 | << std::endl; 24 | return 1; 25 | } 26 | camera_acquired_ = true; 27 | return 0; 28 | } 29 | 30 | char * LibCamera::getCameraId(){ 31 | return cameraId.data(); 32 | } 33 | 34 | void LibCamera::configureStill(int width, int height, PixelFormat format, int buffercount, int rotation) { 35 | printf("Configuring still capture...\n"); 36 | config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); 37 | if (width && height) { 38 | libcamera::Size size(width, height); 39 | config_->at(0).size = size; 40 | } 41 | config_->at(0).pixelFormat = format; 42 | if (buffercount) 43 | config_->at(0).bufferCount = buffercount; 44 | Transform transform = Transform::Identity; 45 | bool ok; 46 | Transform rot = transformFromRotation(rotation, &ok); 47 | if (!ok) 48 | throw std::runtime_error("illegal rotation value, Please use 0 or 180"); 49 | transform = rot * transform; 50 | if (!!(transform & Transform::Transpose)) 51 | throw std::runtime_error("transforms requiring transpose not supported"); 52 | config_->transform = transform; 53 | 54 | CameraConfiguration::Status validation = config_->validate(); 55 | if (validation == CameraConfiguration::Invalid) 56 | throw std::runtime_error("failed to valid stream configurations"); 57 | else if (validation == CameraConfiguration::Adjusted) 58 | std::cout << "Stream configuration adjusted" << std::endl; 59 | 60 | printf("Still capture setup complete\n"); 61 | } 62 | 63 | int LibCamera::startCamera() { 64 | int ret; 65 | ret = camera_->configure(config_.get()); 66 | if (ret < 0) { 67 | std::cout << "Failed to configure camera" << std::endl; 68 | return ret; 69 | } 70 | 71 | camera_->requestCompleted.connect(this, &LibCamera::requestComplete); 72 | 73 | allocator_ = std::make_unique(camera_); 74 | 75 | return startCapture(); 76 | } 77 | 78 | int LibCamera::startCapture() { 79 | int ret; 80 | unsigned int nbuffers = UINT_MAX; 81 | for (StreamConfiguration &cfg : *config_) { 82 | ret = allocator_->allocate(cfg.stream()); 83 | if (ret < 0) { 84 | std::cerr << "Can't allocate buffers" << std::endl; 85 | return -ENOMEM; 86 | } 87 | 88 | unsigned int allocated = allocator_->buffers(cfg.stream()).size(); 89 | nbuffers = std::min(nbuffers, allocated); 90 | } 91 | 92 | for (unsigned int i = 0; i < nbuffers; i++) { 93 | std::unique_ptr request = camera_->createRequest(); 94 | if (!request) { 95 | std::cerr << "Can't create request" << std::endl; 96 | return -ENOMEM; 97 | } 98 | 99 | for (StreamConfiguration &cfg : *config_) { 100 | Stream *stream = cfg.stream(); 101 | const std::vector> &buffers = 102 | allocator_->buffers(stream); 103 | const std::unique_ptr &buffer = buffers[i]; 104 | 105 | ret = request->addBuffer(stream, buffer.get()); 106 | if (ret < 0) { 107 | std::cerr << "Can't set buffer for request" 108 | << std::endl; 109 | return ret; 110 | } 111 | for (const FrameBuffer::Plane &plane : buffer->planes()) { 112 | void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, 113 | plane.fd.get(), 0); 114 | mappedBuffers_[plane.fd.get()] = 115 | std::make_pair(memory, plane.length); 116 | } 117 | } 118 | 119 | requests_.push_back(std::move(request)); 120 | } 121 | 122 | ret = camera_->start(&this->controls_); 123 | // ret = camera_->start(); 124 | if (ret) { 125 | std::cout << "Failed to start capture" << std::endl; 126 | return ret; 127 | } 128 | controls_.clear(); 129 | camera_started_ = true; 130 | for (std::unique_ptr &request : requests_) { 131 | ret = queueRequest(request.get()); 132 | if (ret < 0) { 133 | std::cerr << "Can't queue request" << std::endl; 134 | camera_->stop(); 135 | return ret; 136 | } 137 | } 138 | viewfinder_stream_ = config_->at(0).stream(); 139 | return 0; 140 | } 141 | 142 | void LibCamera::StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const 143 | { 144 | StreamConfiguration const &cfg = stream->configuration(); 145 | if (w) 146 | *w = cfg.size.width; 147 | if (h) 148 | *h = cfg.size.height; 149 | if (stride) 150 | *stride = cfg.stride; 151 | } 152 | 153 | Stream *LibCamera::VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const 154 | { 155 | StreamDimensions(viewfinder_stream_, w, h, stride); 156 | return viewfinder_stream_; 157 | } 158 | 159 | int LibCamera::queueRequest(Request *request) { 160 | std::lock_guard stop_lock(camera_stop_mutex_); 161 | if (!camera_started_) 162 | return -1; 163 | { 164 | std::lock_guard lock(control_mutex_); 165 | request->controls() = std::move(controls_); 166 | } 167 | return camera_->queueRequest(request); 168 | } 169 | 170 | void LibCamera::requestComplete(Request *request) { 171 | if (request->status() == Request::RequestCancelled) 172 | return; 173 | processRequest(request); 174 | } 175 | 176 | void LibCamera::processRequest(Request *request) { 177 | requestQueue.push(request); 178 | } 179 | 180 | void LibCamera::returnFrameBuffer(LibcameraOutData frameData) { 181 | uint64_t request = frameData.request; 182 | Request * req = (Request *)request; 183 | req->reuse(Request::ReuseBuffers); 184 | queueRequest(req); 185 | } 186 | 187 | bool LibCamera::readFrame(LibcameraOutData *frameData){ 188 | std::lock_guard lock(free_requests_mutex_); 189 | // int w, h, stride; 190 | if (!requestQueue.empty()){ 191 | Request *request = this->requestQueue.front(); 192 | 193 | const Request::BufferMap &buffers = request->buffers(); 194 | for (auto it = buffers.begin(); it != buffers.end(); ++it) { 195 | FrameBuffer *buffer = it->second; 196 | for (unsigned int i = 0; i < buffer->planes().size(); ++i) { 197 | const FrameBuffer::Plane &plane = buffer->planes()[i]; 198 | const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; 199 | 200 | void *data = mappedBuffers_[plane.fd.get()].first; 201 | int length = std::min(meta.bytesused, plane.length); 202 | 203 | frameData->size = length; 204 | frameData->imageData = (uint8_t *)data; 205 | } 206 | } 207 | this->requestQueue.pop(); 208 | frameData->request = (uint64_t)request; 209 | return true; 210 | } else { 211 | Request *request = nullptr; 212 | frameData->request = (uint64_t)request; 213 | return false; 214 | } 215 | } 216 | 217 | void LibCamera::set(ControlList controls){ 218 | std::lock_guard lock(control_mutex_); 219 | this->controls_ = std::move(controls); 220 | } 221 | 222 | int LibCamera::resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation) { 223 | stopCamera(); 224 | configureStill(width, height, format, buffercount, rotation); 225 | return startCamera(); 226 | } 227 | 228 | void LibCamera::stopCamera() { 229 | if (camera_){ 230 | { 231 | std::lock_guard lock(camera_stop_mutex_); 232 | if (camera_started_){ 233 | if (camera_->stop()) 234 | throw std::runtime_error("failed to stop camera"); 235 | camera_started_ = false; 236 | } 237 | } 238 | camera_->requestCompleted.disconnect(this, &LibCamera::requestComplete); 239 | } 240 | while (!requestQueue.empty()) 241 | requestQueue.pop(); 242 | 243 | for (auto &iter : mappedBuffers_) 244 | { 245 | std::pair pair_ = iter.second; 246 | munmap(std::get<0>(pair_), std::get<1>(pair_)); 247 | } 248 | 249 | mappedBuffers_.clear(); 250 | 251 | requests_.clear(); 252 | 253 | allocator_.reset(); 254 | 255 | controls_.clear(); 256 | } 257 | 258 | void LibCamera::closeCamera(){ 259 | if (camera_acquired_) 260 | camera_->release(); 261 | camera_acquired_ = false; 262 | 263 | camera_.reset(); 264 | 265 | cm.reset(); 266 | } 267 | -------------------------------------------------------------------------------- /LibCamera.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using namespace libcamera; 31 | 32 | typedef struct { 33 | uint8_t *imageData; 34 | uint32_t size; 35 | uint64_t request; 36 | } LibcameraOutData; 37 | 38 | class LibCamera { 39 | public: 40 | LibCamera(){}; 41 | ~LibCamera(){}; 42 | 43 | int initCamera(); 44 | void configureStill(int width, int height, PixelFormat format, int buffercount, int rotation); 45 | int startCamera(); 46 | int resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation); 47 | bool readFrame(LibcameraOutData *frameData); 48 | void returnFrameBuffer(LibcameraOutData frameData); 49 | 50 | void set(ControlList controls); 51 | void stopCamera(); 52 | void closeCamera(); 53 | 54 | Stream *VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const; 55 | char * getCameraId(); 56 | 57 | private: 58 | int startCapture(); 59 | int queueRequest(Request *request); 60 | void requestComplete(Request *request); 61 | void processRequest(Request *request); 62 | 63 | void StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const; 64 | 65 | unsigned int cameraIndex_; 66 | uint64_t last_; 67 | std::unique_ptr cm; 68 | std::shared_ptr camera_; 69 | bool camera_acquired_ = false; 70 | bool camera_started_ = false; 71 | std::unique_ptr config_; 72 | std::unique_ptr allocator_; 73 | std::vector> requests_; 74 | // std::map stream_; 75 | std::map> mappedBuffers_; 76 | 77 | std::queue requestQueue; 78 | 79 | ControlList controls_; 80 | std::mutex control_mutex_; 81 | std::mutex camera_stop_mutex_; 82 | std::mutex free_requests_mutex_; 83 | 84 | Stream *viewfinder_stream_ = nullptr; 85 | std::string cameraId; 86 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Libcamera c++ demo 2 | 3 | 4 | ``` 5 | sudo apt install -y libopencv-dev 6 | sudo apt install -y cmake 7 | ``` 8 | 9 | ``` 10 | mkdir build 11 | cd build 12 | cmake .. 13 | make -j4 14 | ``` 15 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "LibCamera.h" 7 | 8 | using namespace cv; 9 | 10 | int main() { 11 | time_t start_time = time(0); 12 | int frame_count = 0; 13 | float lens_position = 100; 14 | float focus_step = 50; 15 | LibCamera cam; 16 | uint32_t width = 1920; 17 | uint32_t height = 1080; 18 | uint32_t stride; 19 | char key; 20 | int window_width = 1920; 21 | int window_height = 1080; 22 | 23 | if (width > window_width) 24 | { 25 | cv::namedWindow("libcamera-demo", cv::WINDOW_NORMAL); 26 | cv::resizeWindow("libcamera-demo", window_width, window_height); 27 | } 28 | 29 | int ret = cam.initCamera(); 30 | cam.configureStill(width, height, formats::RGB888, 1, 0); 31 | ControlList controls_; 32 | int64_t frame_time = 1000000 / 10; 33 | // Set frame rate 34 | controls_.set(controls::FrameDurationLimits, libcamera::Span({ frame_time, frame_time })); 35 | // Adjust the brightness of the output images, in the range -1.0 to 1.0 36 | controls_.set(controls::Brightness, 0.5); 37 | // Adjust the contrast of the output image, where 1.0 = normal contrast 38 | controls_.set(controls::Contrast, 1.5); 39 | // Set the exposure time 40 | controls_.set(controls::ExposureTime, 20000); 41 | cam.set(controls_); 42 | if (!ret) { 43 | bool flag; 44 | LibcameraOutData frameData; 45 | cam.startCamera(); 46 | cam.VideoStream(&width, &height, &stride); 47 | while (true) { 48 | flag = cam.readFrame(&frameData); 49 | if (!flag) 50 | continue; 51 | Mat im(height, width, CV_8UC3, frameData.imageData, stride); 52 | 53 | imshow("libcamera-demo", im); 54 | key = waitKey(1); 55 | if (key == 'q') { 56 | break; 57 | } else if (key == 'f') { 58 | ControlList controls; 59 | controls.set(controls::AfMode, controls::AfModeAuto); 60 | controls.set(controls::AfTrigger, 0); 61 | cam.set(controls); 62 | } else if (key == 'a' || key == 'A') { 63 | lens_position += focus_step; 64 | } else if (key == 'd' || key == 'D') { 65 | lens_position -= focus_step; 66 | } 67 | 68 | // To use the manual focus function, libcamera-dev needs to be updated to version 0.0.10 and above. 69 | if (key == 'a' || key == 'A' || key == 'd' || key == 'D') { 70 | ControlList controls; 71 | controls.set(controls::AfMode, controls::AfModeManual); 72 | controls.set(controls::LensPosition, lens_position); 73 | cam.set(controls); 74 | } 75 | 76 | frame_count++; 77 | if ((time(0) - start_time) >= 1){ 78 | printf("fps: %d\n", frame_count); 79 | frame_count = 0; 80 | start_time = time(0); 81 | } 82 | cam.returnFrameBuffer(frameData); 83 | } 84 | destroyAllWindows(); 85 | cam.stopCamera(); 86 | } 87 | cam.closeCamera(); 88 | return 0; 89 | } 90 | --------------------------------------------------------------------------------