Skip to content

Commit 1abdde6

Browse files
committed
work in progress libcamera implementation
1 parent e6ac4a5 commit 1abdde6

5 files changed

Lines changed: 105 additions & 28 deletions

File tree

qml/Application.qml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Qt.labs.platform
88
import CollageModel
99
import Printer
1010
import QtQuick.Window
11+
import Libcamera
1112

1213
ApplicationWindow {
1314
id: mainWindow
@@ -69,6 +70,20 @@ ApplicationWindow {
6970
flow.galleryMenu.printer = printer
7071
}
7172

73+
Libcamera {
74+
id: libcamera
75+
76+
onErrorOccurred: function(errorString) {
77+
if(state === "Libcamera")
78+
{
79+
cameraSource.errorOccurred(errorString)
80+
}
81+
}
82+
onImageCaptured: function(image) {
83+
cameraSource.imageCaptured(image)
84+
}
85+
}
86+
7287
ApplicationFlow
7388
{
7489
id: flow

qml/SettingsMenu.qml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@ SettingsMenuForm {
4646
id: gphotoCamera
4747
}
4848

49-
Libcamera {
50-
id: libcamera
51-
}
52-
5349
MediaDevices
5450
{
5551
id: mediaDevices

qml/content/CameraSource.qml

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import GPhotoCamera
2-
import Libcamera
32
import QtQuick
43
import QtMultimedia
54
import QtQuick.Controls
@@ -38,6 +37,16 @@ Item
3837
return
3938
}
4039
}
40+
var libcameras = libcamera.availableCameras()
41+
for (var j = 0; j < libcameras.length; j++) {
42+
if (libcameras[j] === cameraName) {
43+
libcamera.cameraName = cameraName
44+
cameraSource.state = "Libcamera"
45+
console.log("CameraSource using Libcamera camera device: " + cameraName)
46+
return
47+
}
48+
}
49+
4150
console.log("CameraSource could not find camera device: " + cameraName)
4251
cameraSource.state = "noCamera"
4352
}
@@ -116,20 +125,6 @@ Item
116125
}
117126
}
118127

119-
Libcamera {
120-
id: libcamera
121-
122-
onErrorOccurred: function(errorString) {
123-
if(state === "Libcamera")
124-
{
125-
cameraSource.errorOccurred(errorString)
126-
}
127-
}
128-
onImageCaptured: function(image) {
129-
cameraSource.imageCaptured(image)
130-
}
131-
}
132-
133128
Camera {
134129
id: systemCamera
135130
cameraDevice: mediaDevices.defaultVideoInput
@@ -239,7 +234,7 @@ Item
239234
target: cameraSession
240235
videoFrameInput: libcamera
241236
}
242-
}
237+
},
243238
State {
244239
name: "Error"
245240
PropertyChanges {

src/libcameracamera.cpp

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <libcamera/formats.h>
77
#include <libcamera/framebuffer_allocator.h>
88
#include <QDebug>
9+
#include <sys/mman.h>
910

1011
using namespace libcamera;
1112

@@ -285,16 +286,82 @@ void LibCameraWorker::captureImage()
285286

286287
void LibCameraWorker::queueViewfinderRequest()
287288
{
288-
if (!mCamera) return;
289-
// Similar to captureImage but re-use buffers and emit frameReady for previews.
290-
// TODO: implement re-queueing multiple requests, handle Request::completed, reuse buffers
289+
if (!mCamera || mBuffers.empty()) return;
290+
291+
// Create a request
292+
std::shared_ptr<Request> request = mCamera->createRequest();
293+
if (!request) {
294+
Q_EMIT errorOccurred("libcamera: createRequest failed for preview");
295+
return;
296+
}
297+
298+
// Get next buffer (rotate through available buffers)
299+
FrameBuffer *fb = mBuffers[mBufferIndex % mBuffers.size()].get();
300+
mBufferIndex++;
301+
302+
// Attach buffer to stream
303+
Stream *stream = *mCamera->streams().begin();
304+
if (request->addBuffer(stream, fb) < 0) {
305+
Q_EMIT errorOccurred("libcamera: addBuffer failed for preview");
306+
return;
307+
}
308+
309+
// Store request to keep it alive
310+
mPendingRequests.push_back(request);
311+
312+
// Queue the request (async completion via event loop)
313+
if (mCamera->queueRequest(request.get()) < 0) {
314+
Q_EMIT errorOccurred("libcamera: queueRequest failed for preview");
315+
mPendingRequests.pop_back();
316+
return;
317+
}
318+
319+
// In production libcamera, you'd use a callback/event loop.
320+
// For now, use a simple timer-based poll as fallback:
321+
QTimer::singleShot(50, this, [this, request]() {
322+
if (request->status() == Request::RequestComplete) {
323+
processCompletedRequest(request.get());
324+
// Remove completed request from pending list
325+
auto it = std::find(mPendingRequests.begin(), mPendingRequests.end(), request);
326+
if (it != mPendingRequests.end())
327+
mPendingRequests.erase(it);
328+
// Queue next preview
329+
if (mRunning)
330+
queueViewfinderRequest();
331+
}
332+
});
333+
}
334+
335+
void LibCameraWorker::processCompletedRequest(Request *request)
336+
{
337+
if (!request || request->buffers().empty()) return;
338+
339+
auto it = request->buffers().begin();
340+
FrameBuffer *completedFb = it->second;
341+
342+
// Convert buffer to QImage and emit preview
343+
QImage preview = convertBufferToImage(*completedFb);
344+
if (!preview.isNull())
345+
Q_EMIT frameReady(preview);
291346
}
292347

293348
QImage LibCameraWorker::convertBufferToImage(const FrameBuffer &fb)
294349
{
295-
// TODO: implement conversion from FrameBuffer payload to QImage.
296-
// If fb.pixelFormat() == formats::RGB888 you can construct QImage directly from buffer.
297-
// If YUV, use conversion (libyuv/opencv) to RGB.
298-
// Placeholder empty image:
299-
return QImage();
350+
if (fb.planes().empty()) return QImage();
351+
352+
const FrameMetadata &metadata = fb.metadata();
353+
uint32_t width = metadata.planes()[0].bytesused / 3; // Assume RGB888 (3 bytes per pixel)
354+
uint32_t height = fb.planes()[0].length / metadata.planes()[0].bytesused;
355+
356+
if (width == 0 || height == 0) return QImage();
357+
358+
// Map buffer to accessible
359+
void *data = mmap(NULL, fb.planes()[0].length, PROT_READ | PROT_WRITE, MAP_SHARED, fb.planes()[0].fd.get(), 0);
360+
361+
// Create QImage from RGB888 data (copy data)
362+
QImage image(width, height, QImage::Format_RGB888);
363+
std::memcpy(image.bits(), data, image.sizeInBytes());
364+
365+
return image;
300366
}
367+

src/libcameracamera.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,8 @@ public slots:
8080
std::atomic<bool> mRunning{false};
8181
QTimer mPreviewTimer; // optional periodic preview or parameter checks
8282
int mRequestedWidth{640}, mRequestedHeight{480};
83+
84+
std::vector<std::shared_ptr<libcamera::Request>> mPendingRequests;
85+
size_t mBufferIndex = 0;
86+
void processCompletedRequest(libcamera::Request *request);
8387
};

0 commit comments

Comments
 (0)