From 97e8b3a2eb321884fe1e15fb584f41a38cc33d51 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Mar 2019 03:24:25 +0200 Subject: qcam: Add Qt-based GUI application 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. Only streams in YUYV formats are supported for now. Signed-off-by: Laurent Pinchart Acked-by: Kieran Bingham Tested-by: Kieran Bingham --- src/qcam/main_window.cpp | 228 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 src/qcam/main_window.cpp (limited to 'src/qcam/main_window.cpp') diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp new file mode 100644 index 00000000..a148aa4d --- /dev/null +++ b/src/qcam/main_window.cpp @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * main_window.cpp - qcam - Main application window + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#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(); + } + + 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 &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(options_[OptCamera]); + } + + 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 streams{ stream }; + config_ = camera_->streamConfiguration(streams); + ret = camera_->configureStreams(config_); + if (ret < 0) { + std::cout << "Failed to configure camera" << std::endl; + return ret; + } + + const StreamConfiguration &sconf = config_[stream]; + ret = viewfinder_->setFormat(sconf.pixelFormat, sconf.width, sconf.height); + if (ret < 0) { + std::cout << "Failed to set viewfinder format" << std::endl; + return ret; + } + + adjustSize(); + + ret = camera_->allocateBuffers(); + if (ret) { + std::cerr << "Failed to allocate buffers" + << std::endl; + return ret; + } + + BufferPool &pool = stream->bufferPool(); + std::vector 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 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 &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(plane.mem()); + viewfinder_->display(raw); + + return 0; +} -- cgit v1.2.1