[libcamera-devel] [PATCH 2/2] qcam: Add Qt-based GUI application
Niklas Söderlund
niklas.soderlund at ragnatech.se
Wed Mar 27 01:42:00 CET 2019
Hi Laurent,
Thanks for your work.
On 2019-03-23 09:31:25 +0200, Laurent Pinchart wrote:
> qcam is a sample camera GUI application based on Qt. It demonstrates
> integration of the Qt event loop with libcamera.
>
> The application lets the user select a camera through the GUI, and then
> captures a single stream from the camera and displays it in a window.
Maybe it's worth mentioning in the commit message that qcam only works
for YVYU pixelformats ?
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
> src/meson.build | 1 +
> src/qcam/format_converter.cpp | 99 ++++++++++++++
> src/qcam/format_converter.h | 14 ++
> src/qcam/main.cpp | 75 +++++++++++
> src/qcam/main_window.cpp | 223 +++++++++++++++++++++++++++++++
> src/qcam/main_window.h | 54 ++++++++
> src/qcam/meson.build | 19 +++
> src/qcam/qt_event_dispatcher.cpp | 145 ++++++++++++++++++++
> src/qcam/qt_event_dispatcher.h | 62 +++++++++
> src/qcam/viewfinder.cpp | 38 ++++++
> src/qcam/viewfinder.h | 31 +++++
> 11 files changed, 761 insertions(+)
> create mode 100644 src/qcam/format_converter.cpp
> create mode 100644 src/qcam/format_converter.h
> create mode 100644 src/qcam/main.cpp
> create mode 100644 src/qcam/main_window.cpp
> create mode 100644 src/qcam/main_window.h
> create mode 100644 src/qcam/meson.build
> create mode 100644 src/qcam/qt_event_dispatcher.cpp
> create mode 100644 src/qcam/qt_event_dispatcher.h
> create mode 100644 src/qcam/viewfinder.cpp
> create mode 100644 src/qcam/viewfinder.h
>
> diff --git a/src/meson.build b/src/meson.build
> index a7f2e75de158..4e41fd3e7887 100644
> --- a/src/meson.build
> +++ b/src/meson.build
> @@ -1,2 +1,3 @@
> subdir('libcamera')
> subdir('cam')
> +subdir('qcam')
> diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp
> new file mode 100644
> index 000000000000..0decad74f28e
> --- /dev/null
> +++ b/src/qcam/format_converter.cpp
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * format_convert.cpp - qcam - Convert buffer to RGB
> + */
> +
> +#include <errno.h>
> +
> +#include <linux/videodev2.h>
> +
> +#include "format_converter.h"
> +
> +#define RGBSHIFT 8
> +#ifndef MAX
> +#define MAX(a,b) ((a)>(b)?(a):(b))
> +#endif
> +#ifndef MIN
> +#define MIN(a,b) ((a)<(b)?(a):(b))
> +#endif
> +#ifndef CLAMP
> +#define CLAMP(a,low,high) MAX((low),MIN((high),(a)))
> +#endif
> +#ifndef CLIP
> +#define CLIP(x) CLAMP(x,0,255)
> +#endif
> +
> +static void yuv_to_rgb(int y, int u, int v, int *r, int *g, int *b)
> +{
> + int c = y - 16;
> + int d = u - 128;
> + int e = v - 128;
> + *r = CLIP(( 298 * c + 409 * e + 128) >> RGBSHIFT);
> + *g = CLIP(( 298 * c - 100 * d - 208 * e + 128) >> RGBSHIFT);
> + *b = CLIP(( 298 * c + 516 * d + 128) >> RGBSHIFT);
> +}
> +
> +int buffer_to_rgb32(unsigned char *src, unsigned int format,
> + unsigned int width, unsigned int height,
> + unsigned char *dst)
> +{
> + unsigned int src_x, src_y, dst_x, dst_y;
> + unsigned int src_stride;
> + unsigned int dst_stride;
> + int y_pos, cb_pos, cr_pos;
> + int r, g, b, y, cr, cb;
> +
> + switch (format) {
> + case V4L2_PIX_FMT_VYUY:
> + y_pos = 1;
> + cb_pos = 2;
> + break;
> + case V4L2_PIX_FMT_YVYU:
> + y_pos = 0;
> + cb_pos = 3;
> + break;
> + case V4L2_PIX_FMT_UYVY:
> + y_pos = 1;
> + cb_pos = 0;
> + break;
> + case V4L2_PIX_FMT_YUYV:
> + y_pos = 0;
> + cb_pos = 1;
> + break;
> + default:
> + return -EINVAL;
> + };
> +
> + cr_pos = (cb_pos + 2) % 4;
> + src_stride = width * 2;
> + dst_stride = width * 4;
> +
> + for (src_y = 0, dst_y = 0; dst_y < height; src_y++, dst_y++) {
> + for (src_x = 0, dst_x = 0; dst_x < width; ) {
> + cb = src[src_y * src_stride + src_x * 4 + cb_pos];
> + cr = src[src_y * src_stride + src_x * 4 + cr_pos];
> +
> + y = src[src_y * src_stride + src_x * 4 + y_pos];
> + yuv_to_rgb(y, cb, cr, &r, &g, &b);
> + dst[dst_y * dst_stride + 4 * dst_x + 0] = b;
> + dst[dst_y * dst_stride + 4 * dst_x + 1] = g;
> + dst[dst_y * dst_stride + 4 * dst_x + 2] = r;
> + dst[dst_y * dst_stride + 4 * dst_x + 3] = 0xff;
> + dst_x++;
> +
> + y = src[src_y * src_stride + src_x * 4 + y_pos + 2];
> + yuv_to_rgb(y, cb, cr, &r, &g, &b);
> + dst[dst_y * dst_stride + 4 * dst_x + 0] = b;
> + dst[dst_y * dst_stride + 4 * dst_x + 1] = g;
> + dst[dst_y * dst_stride + 4 * dst_x + 2] = r;
> + dst[dst_y * dst_stride + 4 * dst_x + 3] = 0xff;
> + dst_x++;
> +
> + src_x++;
> + }
> + }
> +
> + return 0;
> +}
> diff --git a/src/qcam/format_converter.h b/src/qcam/format_converter.h
> new file mode 100644
> index 000000000000..3904b139a4fd
> --- /dev/null
> +++ b/src/qcam/format_converter.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * format_convert.h - qcam - Convert buffer to RGB
> + */
> +#ifndef __QCAM_FORMAT_CONVERTER_H__
> +#define __QCAM_FORMAT_CONVERTER_H__
> +
> +int buffer_to_rgb32(unsigned char *src, unsigned int format,
> + unsigned int width, unsigned int height,
> + unsigned char *dst);
> +
> +#endif /* __QCAM_FORMAT_CONVERTER_H__ */
> diff --git a/src/qcam/main.cpp b/src/qcam/main.cpp
> new file mode 100644
> index 000000000000..106b8a162d9f
> --- /dev/null
> +++ b/src/qcam/main.cpp
> @@ -0,0 +1,75 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * main.cpp - cam - The libcamera swiss army knife
> + */
> +
> +#include <iostream>
> +#include <signal.h>
> +#include <string.h>
> +
> +#include <QApplication>
> +
> +#include <libcamera/camera_manager.h>
> +
> +#include "main_window.h"
> +#include "../cam/options.h"
> +#include "qt_event_dispatcher.h"
> +
> +void signalHandler(int signal)
> +{
> + std::cout << "Exiting" << std::endl;
> + qApp->quit();
> +}
> +
> +OptionsParser::Options parseOptions(int argc, char *argv[])
> +{
> + OptionsParser parser;
> + parser.addOption(OptCamera, OptionString,
> + "Specify which camera to operate on", "camera",
> + ArgumentRequired, "camera");
> + parser.addOption(OptHelp, OptionNone, "Display this help message",
> + "help");
> +
> + OptionsParser::Options options = parser.parse(argc, argv);
> + if (options.isSet(OptHelp))
> + parser.usage();
> +
> + return options;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + QApplication app(argc, argv);
> + int ret;
> +
> + OptionsParser::Options options = parseOptions(argc, argv);
> + if (!options.valid())
> + return EXIT_FAILURE;
> + if (options.isSet(OptHelp))
> + return 0;
> +
> + struct sigaction sa = {};
> + sa.sa_handler = &signalHandler;
> + sigaction(SIGINT, &sa, nullptr);
> +
> + std::unique_ptr<EventDispatcher> dispatcher(new QtEventDispatcher());
> + CameraManager *cm = CameraManager::instance();
> + cm->setEventDispatcher(std::move(dispatcher));
> +
> + ret = cm->start();
> + if (ret) {
> + std::cout << "Failed to start camera manager: "
> + << strerror(-ret) << std::endl;
> + return EXIT_FAILURE;
> + }
> +
> + MainWindow *mainWindow = new MainWindow(options);
> + mainWindow->show();
> + ret = app.exec();
> + delete mainWindow;
> +
> + cm->stop();
> + return ret;
> +}
> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
> new file mode 100644
> index 000000000000..d39dead76b56
> --- /dev/null
> +++ b/src/qcam/main_window.cpp
> @@ -0,0 +1,223 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * main_window.cpp - qcam - Main application window
> + */
> +
> +#include <iomanip>
> +#include <iostream>
> +#include <string>
> +
> +#include <QCoreApplication>
> +#include <QInputDialog>
> +#include <QTimer>
> +
> +#include <libcamera/camera_manager.h>
> +
> +#include "main_window.h"
> +#include "viewfinder.h"
> +
> +using namespace libcamera;
> +
> +MainWindow::MainWindow(const OptionsParser::Options &options)
> + : options_(options), isCapturing_(false)
> +{
> + int ret;
> +
> + viewfinder_ = new ViewFinder(this);
> + setCentralWidget(viewfinder_);
> + viewfinder_->setFixedSize(500, 500);
> + adjustSize();
> +
> + ret = openCamera();
> + if (!ret)
> + ret = startCapture();
> +
> + if (ret < 0)
> + QTimer::singleShot(0, QCoreApplication::instance(),
> + &QCoreApplication::quit);
> +}
> +
> +MainWindow::~MainWindow()
> +{
> + if (camera_) {
> + stopCapture();
> + camera_->release();
> + camera_.reset();
I know both release() and reset() is needed but it looks but ugly :-P
> + }
> +
> + CameraManager::instance()->stop();
> +}
> +
> +int MainWindow::openCamera()
> +{
> + CameraManager *cm = CameraManager::instance();
> + std::string cameraName;
> +
> + if (!options_.isSet(OptCamera)) {
> + QStringList cameras;
> + bool result;
> +
> + for (const std::shared_ptr<Camera> &cam : cm->cameras())
> + cameras.append(QString::fromStdString(cam->name()));
> +
> + QString name = QInputDialog::getItem(this, "Select Camera",
> + "Camera:", cameras, 0,
> + false, &result);
> + if (!result)
> + return -EINVAL;
> +
> + cameraName = name.toStdString();
> + } else {
> + cameraName = static_cast<std::string>(options_[OptCamera]);
Is this static_cast really needed? We have context here so the compiler
should do the right thing and use the operator std::string, right? The
only time I found my self the need to cast this is when redirecting it
to std::c{err,out} as no context exists so it picks operator int.
> + }
> +
> + camera_ = cm->get(cameraName);
> + if (!camera_) {
> + std::cout << "Camera " << cameraName << " not found"
> + << std::endl;
> + return -ENODEV;
> + }
> +
> + if (camera_->acquire()) {
> + std::cout << "Failed to acquire camera" << std::endl;
> + camera_.reset();
> + return -EBUSY;
> + }
> +
> + std::cout << "Using camera " << camera_->name() << std::endl;
> +
> + camera_->requestCompleted.connect(this, &MainWindow::requestComplete);
> +
> + return 0;
> +}
> +
> +int MainWindow::startCapture()
> +{
> + int ret;
> +
> + Stream *stream = *camera_->streams().begin();
> + std::set<Stream *> streams{ stream };
> + config_ = camera_->streamConfiguration(streams);
This will conflict with my upcoming work to add stream usage hints, but
it should be easy to handle. Please scratch my fears discussed on IRC I
think we should merge qcam as soon as it's ready.
> + ret = camera_->configureStreams(config_);
> + if (ret < 0) {
> + std::cout << "Failed to configure camera" << std::endl;
> + return ret;
> + }
> +
> + const StreamConfiguration &sconf = config_[stream];
> + viewfinder_->setFormat(sconf.pixelFormat, sconf.width, sconf.height);
> + adjustSize();
> +
> + ret = camera_->allocateBuffers();
> + if (ret) {
> + std::cerr << "Failed to allocate buffers"
> + << std::endl;
> + return ret;
> + }
> +
> + BufferPool &pool = stream->bufferPool();
> + std::vector<Request *> requests;
> +
> + for (Buffer &buffer : pool.buffers()) {
> + Request *request = camera_->createRequest();
> + if (!request) {
> + std::cerr << "Can't create request" << std::endl;
> + ret = -ENOMEM;
> + goto error;
> + }
> +
> + std::map<Stream *, Buffer *> map;
> + map[stream] = &buffer;
> + ret = request->setBuffers(map);
> + if (ret < 0) {
> + std::cerr << "Can't set buffers for request" << std::endl;
> + goto error;
> + }
> +
> + requests.push_back(request);
> + }
> +
> + ret = camera_->start();
> + if (ret) {
> + std::cout << "Failed to start capture" << std::endl;
> + goto error;
> + }
> +
> + for (Request *request : requests) {
> + ret = camera_->queueRequest(request);
> + if (ret < 0) {
> + std::cerr << "Can't queue request" << std::endl;
> + goto error;
> + }
> + }
> +
> + isCapturing_ = true;
> + return 0;
> +
> +error:
> + for (Request *request : requests)
> + delete request;
> +
> + camera_->freeBuffers();
> + return ret;
> +}
> +
> +void MainWindow::stopCapture()
> +{
> + if (!isCapturing_)
> + return;
> +
> + int ret = camera_->stop();
> + if (ret)
> + std::cout << "Failed to stop capture" << std::endl;
> +
> + camera_->freeBuffers();
> + isCapturing_ = false;
> +}
> +
> +void MainWindow::requestComplete(Request *request,
> + const std::map<Stream *, Buffer *> &buffers)
> +{
> + static uint64_t last = 0;
> +
> + if (request->status() == Request::RequestCancelled)
> + return;
> +
> + Buffer *buffer = buffers.begin()->second;
> +
> + double fps = buffer->timestamp() - last;
> + fps = last && fps ? 1000000000.0 / fps : 0.0;
> + last = buffer->timestamp();
> +
> + std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
> + << " buf: " << buffer->index()
> + << " bytesused: " << buffer->bytesused()
> + << " timestamp: " << buffer->timestamp()
> + << " fps: " << std::fixed << std::setprecision(2) << fps
> + << std::endl;
> +
> + display(buffer);
> +
> + request = camera_->createRequest();
> + if (!request) {
> + std::cerr << "Can't create request" << std::endl;
> + return;
> + }
> +
> + request->setBuffers(buffers);
> + camera_->queueRequest(request);
> +}
> +
> +int MainWindow::display(Buffer *buffer)
> +{
> + if (buffer->planes().size() != 1)
> + return -EINVAL;
> +
> + Plane &plane = buffer->planes().front();
> + unsigned char *raw = static_cast<unsigned char *>(plane.mem());
> + viewfinder_->display(raw);
> +
> + return 0;
> +}
> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h
> new file mode 100644
> index 000000000000..5e27a8fd6b4e
> --- /dev/null
> +++ b/src/qcam/main_window.h
> @@ -0,0 +1,54 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * main_window.h - qcam - Main application window
> + */
> +#ifndef __QCAM_MAIN_WINDOW_H__
> +#define __QCAM_MAIN_WINDOW_H__
> +
> +#include <map>
> +
> +#include <QMainWindow>
> +
> +#include <libcamera/camera.h>
> +#include <libcamera/stream.h>
> +
> +#include "../cam/options.h"
> +
> +using namespace libcamera;
> +
> +class ViewFinder;
> +
> +enum {
> + OptCamera = 'c',
> + OptHelp = 'h',
> +};
> +
> +class MainWindow : public QMainWindow
> +{
> +public:
> + MainWindow(const OptionsParser::Options &options);
> + ~MainWindow();
> +
> +private:
> + int openCamera();
> +
> + int startCapture();
> + int configureStreams(Camera *camera, std::set<Stream *> &streams);
> + void stopCapture();
> +
> + void requestComplete(Request *request,
> + const std::map<Stream *, Buffer *> &buffers);
> + int display(Buffer *buffer);
> +
> + const OptionsParser::Options &options_;
> +
> + std::shared_ptr<Camera> camera_;
> + bool isCapturing_;
> + std::map<Stream *, StreamConfiguration> config_;
> +
> + ViewFinder *viewfinder_;
> +};
> +
> +#endif /* __QCAM_MAIN_WINDOW__ */
> diff --git a/src/qcam/meson.build b/src/qcam/meson.build
> new file mode 100644
> index 000000000000..8a71cda3dfe5
> --- /dev/null
> +++ b/src/qcam/meson.build
> @@ -0,0 +1,19 @@
> +qcam_sources = files([
> + 'format_converter.cpp',
> + 'main.cpp',
> + 'main_window.cpp',
> + '../cam/options.cpp',
> + 'qt_event_dispatcher.cpp',
> + 'viewfinder.cpp',
> +])
> +
> +import('qt5')
> +qt5_dep = dependency('qt5', modules: ['Core', 'Gui', 'Widgets'], required : false)
> +
> +if qt5_dep.found()
> + qcam = executable('qcam', qcam_sources,
> + link_with : libcamera,
> + include_directories : libcamera_includes,
> + dependencies : qt5_dep,
> + cpp_args : '-DQT_NO_KEYWORDS')
> +endif
> diff --git a/src/qcam/qt_event_dispatcher.cpp b/src/qcam/qt_event_dispatcher.cpp
> new file mode 100644
> index 000000000000..5ba451bf88ce
> --- /dev/null
> +++ b/src/qcam/qt_event_dispatcher.cpp
> @@ -0,0 +1,145 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * qt_event_dispatcher.cpp - qcam - Qt-based event dispatcher
> + */
> +
> +#include <iostream>
> +
> +#include <QAbstractEventDispatcher>
> +#include <QCoreApplication>
> +#include <QSocketNotifier>
> +#include <QTimerEvent>
> +
> +#include <libcamera/event_notifier.h>
> +#include <libcamera/timer.h>
> +
> +#include "qt_event_dispatcher.h"
> +
> +using namespace libcamera;
> +
> +QtEventDispatcher::QtEventDispatcher()
> +{
> +}
> +
> +QtEventDispatcher::~QtEventDispatcher()
> +{
> + for (auto &it : notifiers_) {
> + NotifierSet &set = it.second;
> + delete set.read.qnotifier;
> + delete set.write.qnotifier;
> + delete set.exception.qnotifier;
> + }
> +}
> +
> +void QtEventDispatcher::registerEventNotifier(EventNotifier *notifier)
> +{
> + NotifierSet &set = notifiers_[notifier->fd()];
> + QSocketNotifier::Type qtype;
> + void (QtEventDispatcher::*method)(int);
> + NotifierPair *pair;
> +
> + switch (notifier->type()) {
> + case EventNotifier::Read:
> + default:
> + qtype = QSocketNotifier::Read;
> + method = &QtEventDispatcher::readNotifierActivated;
> + pair = &set.read;
> + break;
> +
> + case EventNotifier::Write:
> + qtype = QSocketNotifier::Write;
> + method = &QtEventDispatcher::writeNotifierActivated;
> + pair = &set.write;
> + break;
> +
> + case EventNotifier::Exception:
> + qtype = QSocketNotifier::Exception;
> + method = &QtEventDispatcher::exceptionNotifierActivated;
> + pair = &set.exception;
> + break;
> + }
> +
> + QSocketNotifier *qnotifier = new QSocketNotifier(notifier->fd(), qtype);
> + connect(qnotifier, &QSocketNotifier::activated, this, method);
> + pair->notifier = notifier;
> + pair->qnotifier = qnotifier;
> +}
> +
> +void QtEventDispatcher::unregisterEventNotifier(EventNotifier *notifier)
> +{
> + NotifierSet &set = notifiers_[notifier->fd()];
> + NotifierPair *pair;
> +
> + switch (notifier->type()) {
> + case EventNotifier::Read:
> + default:
> + pair = &set.read;
> + break;
> +
> + case EventNotifier::Write:
> + pair = &set.write;
> + break;
> +
> + case EventNotifier::Exception:
> + pair = &set.exception;
> + break;
> + }
> +
> + delete pair->qnotifier;
> + pair->qnotifier = nullptr;
> + pair->notifier = nullptr;
> +}
> +
> +void QtEventDispatcher::readNotifierActivated(int socket)
> +{
> + EventNotifier *notifier = notifiers_[socket].read.notifier;
> + notifier->activated.emit(notifier);
> +}
> +
> +void QtEventDispatcher::writeNotifierActivated(int socket)
> +{
> + EventNotifier *notifier = notifiers_[socket].write.notifier;
> + notifier->activated.emit(notifier);
> +}
> +
> +void QtEventDispatcher::exceptionNotifierActivated(int socket)
> +{
> + EventNotifier *notifier = notifiers_[socket].exception.notifier;
> + notifier->activated.emit(notifier);
> +}
> +
> +void QtEventDispatcher::registerTimer(Timer *timer)
> +{
> + int timerId = startTimer(timer->interval());
> + timers_[timerId] = timer;
> + timerIds_[timer] = timerId;
> +}
> +
> +void QtEventDispatcher::unregisterTimer(Timer *timer)
> +{
> + auto it = timerIds_.find(timer);
> + timers_.erase(it->second);
> + killTimer(it->second);
> + timerIds_.erase(it);
> +}
> +
> +void QtEventDispatcher::timerEvent(QTimerEvent *event)
> +{
> + auto it = timers_.find(event->timerId());
> + timerIds_.erase(it->second);
> + killTimer(it->first);
> + timers_.erase(it);
> +}
> +
> +void QtEventDispatcher::processEvents()
> +{
> + std::cout << "QtEventDispatcher::processEvents() should not be called"
> + << std::endl;
> +}
> +
> +void QtEventDispatcher::interrupt()
> +{
> + QCoreApplication::eventDispatcher()->interrupt();
> +}
> diff --git a/src/qcam/qt_event_dispatcher.h b/src/qcam/qt_event_dispatcher.h
> new file mode 100644
> index 000000000000..b0f123e52d06
> --- /dev/null
> +++ b/src/qcam/qt_event_dispatcher.h
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * qt_event_dispatcher.h - qcam - Qt-based event dispatcher
> + */
> +#ifndef __QCAM_QT_EVENT_DISPATCHER_H__
> +#define __QCAM_QT_EVENT_DISPATCHER_H__
> +
> +#include <map>
> +
> +#include <libcamera/event_dispatcher.h>
> +
> +using namespace libcamera;
> +
> +class QSocketNotifier;
> +
> +class QtEventDispatcher final : public EventDispatcher, public QObject
> +{
> +public:
> + QtEventDispatcher();
> + ~QtEventDispatcher();
> +
> + void registerEventNotifier(EventNotifier *notifier);
> + void unregisterEventNotifier(EventNotifier *notifier);
> +
> + void registerTimer(Timer *timer);
> + void unregisterTimer(Timer *timer);
> +
> + void processEvents();
> +
> + void interrupt();
> +
> +protected:
> + void timerEvent(QTimerEvent *event);
> +
> +private:
> + void readNotifierActivated(int socket);
> + void writeNotifierActivated(int socket);
> + void exceptionNotifierActivated(int socket);
> +
> + struct NotifierPair {
> + NotifierPair()
> + : notifier(nullptr), qnotifier(nullptr)
> + {
> + }
> + EventNotifier *notifier;
> + QSocketNotifier *qnotifier;
> + };
> +
> + struct NotifierSet {
> + NotifierPair read;
> + NotifierPair write;
> + NotifierPair exception;
> + };
> +
> + std::map<int, NotifierSet> notifiers_;
> + std::map<int, Timer *> timers_;
> + std::map<Timer *, int> timerIds_;
> +};
> +
> +#endif /* __QCAM_QT_EVENT_DISPATCHER_H__ */
> diff --git a/src/qcam/viewfinder.cpp b/src/qcam/viewfinder.cpp
> new file mode 100644
> index 000000000000..5db55ebbf098
> --- /dev/null
> +++ b/src/qcam/viewfinder.cpp
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * viewfinder.cpp - qcam - Viewfinder
> + */
> +
> +#include <QImage>
> +#include <QPixmap>
> +
> +#include "format_converter.h"
> +#include "viewfinder.h"
> +
> +ViewFinder::ViewFinder(QWidget *parent)
> + : QLabel(parent), format_(0), width_(0), height_(0), image_(nullptr)
> +{
> +}
> +
> +void ViewFinder::display(unsigned char *raw)
> +{
> + buffer_to_rgb32(raw, format_, width_, height_, image_->bits());
You should check the return value here as if a none YVYU format is used
it will return -EINVAL and no image will be displayed which might
confuse the user.
> +
> + QPixmap pixmap = QPixmap::fromImage(*image_);
> + setPixmap(pixmap);
> +}
> +
> +void ViewFinder::setFormat(unsigned int format, unsigned int width,
> + unsigned int height)
> +{
> + format_ = format;
> + width_ = width;
> + height_ = height;
> +
> + setFixedSize(width, height);
> +
> + delete image_;
> + image_ = new QImage(width, height, QImage::Format_RGB32);
> +}
> diff --git a/src/qcam/viewfinder.h b/src/qcam/viewfinder.h
> new file mode 100644
> index 000000000000..ecae290a5043
> --- /dev/null
> +++ b/src/qcam/viewfinder.h
> @@ -0,0 +1,31 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * viewfinder.h - qcam - Viewfinder
> + */
> +#ifndef __QCAM_VIEWFINDER_H__
> +#define __QCAM_VIEWFINDER_H__
> +
> +#include <QLabel>
> +
> +class QImage;
> +
> +class ViewFinder : public QLabel
> +{
> +public:
> + ViewFinder(QWidget *parent);
> +
> + void setFormat(unsigned int format, unsigned int width,
> + unsigned int height);
> + void display(unsigned char *rgb);
> +
> +private:
> + unsigned int format_;
> + unsigned int width_;
> + unsigned int height_;
> +
> + QImage *image_;
> +};
> +
> +#endif /* __QCAM_VIEWFINDER__ */
> --
> Regards,
>
> Laurent Pinchart
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
--
Regards,
Niklas Söderlund
More information about the libcamera-devel
mailing list