Skip to content

Commit 6e8edf5

Browse files
committed
early version of libcamera implementation
1 parent 8fe0a5d commit 6e8edf5

3 files changed

Lines changed: 256 additions & 1 deletion

File tree

qtbooth.pro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CONFIG += c++17 qml_debug
44

55
!contains(QT_CONFIG, no-pkg-config) {
66
CONFIG += link_pkgconfig
7-
PKGCONFIG += opencv4
7+
PKGCONFIG += opencv4 libcamera
88
} else {
99
LIBS += -lopencv_core -lopencv_imgproc -lopencv_imgcodecs
1010
}
@@ -18,6 +18,7 @@ SOURCES += src/collageiconmodel.cpp \
1818
src/filesystem.cpp \
1919
src/gphotocamera.cpp \
2020
src/gpio.cpp \
21+
src/libcameracamera.cpp \
2122
src/main.cpp \
2223
src/modelparser.cpp \
2324
src/noprinter.cpp \
@@ -68,6 +69,7 @@ HEADERS += \
6869
src/filesystem.h \
6970
src/gphotocamera.h \
7071
src/gpio.h \
72+
src/libcameracamera.h \
7173
src/modelparser.h \
7274
src/noprinter.h \
7375
src/printerfactory.h \

src/libcameracamera.cpp

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#include "libcameracamera.h"
2+
#include <QThread>
3+
#include <QDebug>
4+
#include <chrono>
5+
#include <thread>
6+
#include <libcamera/formats.h>
7+
#include <libcamera/framebuffer_allocator.h>
8+
9+
using namespace libcamera;
10+
11+
LibCameraWorker::LibCameraWorker(QObject *parent)
12+
: QObject(parent)
13+
{
14+
initCameraManager();
15+
connect(&mPreviewTimer, &QTimer::timeout, this, [this](){
16+
if (mRunning && mCamera)
17+
queueViewfinderRequest(); // optional: grab periodic preview frames
18+
});
19+
}
20+
21+
LibCameraWorker::~LibCameraWorker()
22+
{
23+
stopCamera();
24+
if (mCameraManager)
25+
mCameraManager->stop();
26+
}
27+
28+
void LibCameraWorker::initCameraManager()
29+
{
30+
mCameraManager = std::make_unique<CameraManager>();
31+
int ret = mCameraManager->start();
32+
if (ret) {
33+
Q_EMIT errorOccurred(QString::asprintf("libcamera: CameraManager start failed: %d", ret));
34+
mCameraManager.reset();
35+
}
36+
}
37+
38+
QStringList LibCameraWorker::availableCameras() const
39+
{
40+
QStringList list;
41+
if (!mCameraManager) return list;
42+
for (auto const &id : mCameraManager->cameras())
43+
list << QString::fromStdString(id->id());
44+
return list;
45+
}
46+
47+
void LibCameraWorker::startCamera(const QString &cameraId)
48+
{
49+
if (!mCameraManager) {
50+
Q_EMIT errorOccurred("libcamera: CameraManager not initialized");
51+
return;
52+
}
53+
54+
if (mRunning) stopCamera();
55+
56+
// find camera by id
57+
std::shared_ptr<Camera> cam = mCameraManager->get(cameraId.toStdString());
58+
if (!cam) {
59+
Q_EMIT errorOccurred("libcamera: camera not found: " + cameraId);
60+
return;
61+
}
62+
mCamera = cam;
63+
64+
// configure camera for viewfinder role (basic example)
65+
std::unique_ptr<CameraConfiguration> config =
66+
mCamera->generateConfiguration({ StreamRole::Viewfinder });
67+
if (!config) {
68+
Q_EMIT errorOccurred("libcamera: failed to generate configuration");
69+
mCamera.reset();
70+
return;
71+
}
72+
73+
// choose size; you can support both 320 and 640 by selecting based on requested size
74+
config->at(0).pixelFormat = formats::RGB888; // prefer RGB if supported; else use YUV and convert
75+
config->at(0).size.width = mRequestedWidth;
76+
config->at(0).size.height = mRequestedHeight;
77+
config->at(0).bufferCount = 4;
78+
79+
if (config->validate() == CameraConfiguration::Invalid) {
80+
Q_EMIT errorOccurred("libcamera: configuration invalid");
81+
mCamera.reset();
82+
return;
83+
}
84+
85+
if (mCamera->configure(config.get()) < 0) {
86+
Q_EMIT errorOccurred("libcamera: camera configure failed");
87+
mCamera.reset();
88+
return;
89+
}
90+
91+
// allocate buffers
92+
mAllocator = std::make_unique<FrameBufferAllocator>(mCamera);
93+
for (StreamConfiguration &cfg : *config) {
94+
Stream *stream = cfg.stream();
95+
if (mAllocator->allocate(stream) < 0) {
96+
Q_EMIT errorOccurred("libcamera: buffer allocation failed");
97+
mCamera.reset();
98+
return;
99+
}
100+
const std::vector<std::unique_ptr<FrameBuffer>> &bufs = mAllocator->buffers(stream);
101+
for (auto &b : bufs)
102+
mBuffers.push_back(std::unique_ptr<FrameBuffer>(b.get()));
103+
}
104+
105+
// start camera
106+
if (mCamera->start() < 0) {
107+
Q_EMIT errorOccurred("libcamera: start failed");
108+
mCamera.reset();
109+
return;
110+
}
111+
112+
mRunning = true;
113+
mPreviewTimer.start(100); // optional: request preview every 100ms
114+
}
115+
116+
void LibCameraWorker::stopCamera()
117+
{
118+
if (!mRunning) return;
119+
120+
mPreviewTimer.stop();
121+
122+
if (mCamera) {
123+
mCamera->stop();
124+
if (mAllocator) {
125+
// free buffers
126+
mAllocator.reset();
127+
}
128+
mCamera.reset();
129+
}
130+
mBuffers.clear();
131+
mRunning = false;
132+
}
133+
134+
void LibCameraWorker::captureImage()
135+
{
136+
if (!mRunning || !mCamera) {
137+
Q_EMIT errorOccurred("libcamera: camera not running");
138+
return;
139+
}
140+
141+
// Create a request and keep it alive until completion
142+
std::unique_ptr<Request> request = mCamera->createRequest();
143+
if (!request) {
144+
Q_EMIT errorOccurred("libcamera: createRequest failed");
145+
return;
146+
}
147+
148+
// attach first available buffer for viewfinder stream (simplified)
149+
Stream *stream = *mCamera->streams().begin();
150+
if (mBuffers.empty()) {
151+
Q_EMIT errorOccurred("libcamera: no buffers available");
152+
return;
153+
}
154+
FrameBuffer *fb = mBuffers.front().get();
155+
if (request->addBuffer(stream, fb) < 0) {
156+
Q_EMIT errorOccurred("libcamera: addBuffer failed");
157+
return;
158+
}
159+
160+
// queue request
161+
if (mCamera->queueRequest(request.get()) < 0) {
162+
Q_EMIT errorOccurred("libcamera: queueRequest failed");
163+
return;
164+
}
165+
166+
// wait for completion (polling). This runs in the worker thread so blocking is acceptable.
167+
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
168+
while (std::chrono::steady_clock::now() < deadline) {
169+
if (request->status() != Request::RequestPending)
170+
break;
171+
std::this_thread::sleep_for(std::chrono::milliseconds(5));
172+
}
173+
174+
if (request->status() != Request::RequestComplete) {
175+
Q_EMIT errorOccurred("libcamera: capture failed or timed out");
176+
// let request be destroyed / cleaned up
177+
return;
178+
}
179+
180+
// find buffer and convert
181+
if (!request->buffers().empty()) {
182+
auto it = request->buffers().begin();
183+
FrameBuffer *completedFb = it->second;
184+
QImage img = convertBufferToImage(*completedFb);
185+
Q_EMIT imageCaptured(img);
186+
}
187+
// request destroyed when leaving scope
188+
}
189+
190+
void LibCameraWorker::queueViewfinderRequest()
191+
{
192+
if (!mCamera) return;
193+
// Similar to captureImage but re-use buffers and emit frameReady for previews.
194+
// TODO: implement re-queueing multiple requests, handle Request::completed, reuse buffers
195+
}
196+
197+
QImage LibCameraWorker::convertBufferToImage(const FrameBuffer &fb)
198+
{
199+
// TODO: implement conversion from FrameBuffer payload to QImage.
200+
// If fb.pixelFormat() == formats::RGB888 you can construct QImage directly from buffer.
201+
// If YUV, use conversion (libyuv/opencv) to RGB.
202+
// Placeholder empty image:
203+
return QImage();
204+
}

src/libcameracamera.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#pragma once
2+
#include <QObject>
3+
#include <QImage>
4+
#include <QTimer>
5+
#include <QStringList>
6+
#include <memory>
7+
8+
#undef emit
9+
#undef slots
10+
#include <libcamera/camera_manager.h>
11+
#include <libcamera/camera.h>
12+
#include <libcamera/controls.h>
13+
#include <libcamera/framebuffer_allocator.h>
14+
#define slots Q_SLOTS
15+
#define emit Q_EMIT
16+
17+
class LibCameraWorker : public QObject
18+
{
19+
Q_OBJECT
20+
public:
21+
explicit LibCameraWorker(QObject *parent = nullptr);
22+
~LibCameraWorker() override;
23+
24+
public Q_SLOTS:
25+
void startCamera(const QString &cameraId); // cameraId as returned by CameraManager
26+
void stopCamera();
27+
void captureImage(); // single capture -> emits imageCaptured
28+
QStringList availableCameras() const;
29+
30+
Q_SIGNALS:
31+
void frameReady(const QImage &image); // optionally emit preview frames
32+
void imageCaptured(const QImage &image); // emitted after captureImage
33+
void errorOccurred(const QString &err);
34+
35+
private:
36+
void initCameraManager();
37+
void configureCamera(int width, int height);
38+
void queueViewfinderRequest();
39+
QImage convertBufferToImage(const libcamera::FrameBuffer &fb);
40+
41+
std::unique_ptr<libcamera::CameraManager> mCameraManager;
42+
std::shared_ptr<libcamera::Camera> mCamera;
43+
std::unique_ptr<libcamera::FrameBufferAllocator> mAllocator;
44+
45+
std::vector<std::unique_ptr<libcamera::FrameBuffer>> mBuffers;
46+
std::atomic<bool> mRunning{false};
47+
QTimer mPreviewTimer; // optional periodic preview or parameter checks
48+
int mRequestedWidth{640}, mRequestedHeight{480};
49+
};

0 commit comments

Comments
 (0)