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 <laurent.pinchart@ideasonboard.com> Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
parent
ffef90a1c1
commit
97e8b3a2eb
11 changed files with 793 additions and 0 deletions
228
src/qcam/main_window.cpp
Normal file
228
src/qcam/main_window.cpp
Normal file
|
@ -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 <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();
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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);
|
||||
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<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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue