summaryrefslogtreecommitdiff
path: root/src/apps
diff options
context:
space:
mode:
Diffstat (limited to 'src/apps')
-rw-r--r--src/apps/cam/camera_session.cpp69
-rw-r--r--src/apps/cam/camera_session.h2
-rw-r--r--src/apps/cam/capture_script.cpp2
-rw-r--r--src/apps/cam/capture_script.h2
-rw-r--r--src/apps/cam/drm.cpp2
-rw-r--r--src/apps/cam/drm.h2
-rw-r--r--src/apps/cam/file_sink.cpp62
-rw-r--r--src/apps/cam/file_sink.h20
-rw-r--r--src/apps/cam/frame_sink.cpp2
-rw-r--r--src/apps/cam/frame_sink.h2
-rw-r--r--src/apps/cam/kms_sink.cpp2
-rw-r--r--src/apps/cam/kms_sink.h2
-rw-r--r--src/apps/cam/main.cpp6
-rw-r--r--src/apps/cam/main.h2
-rw-r--r--src/apps/cam/sdl_sink.cpp2
-rw-r--r--src/apps/cam/sdl_sink.h2
-rw-r--r--src/apps/cam/sdl_texture.cpp2
-rw-r--r--src/apps/cam/sdl_texture.h2
-rw-r--r--src/apps/cam/sdl_texture_mjpg.cpp2
-rw-r--r--src/apps/cam/sdl_texture_mjpg.h2
-rw-r--r--src/apps/cam/sdl_texture_yuv.cpp2
-rw-r--r--src/apps/cam/sdl_texture_yuv.h2
-rw-r--r--src/apps/common/dng_writer.cpp225
-rw-r--r--src/apps/common/dng_writer.h3
-rw-r--r--src/apps/common/event_loop.cpp66
-rw-r--r--src/apps/common/event_loop.h25
-rw-r--r--src/apps/common/image.cpp2
-rw-r--r--src/apps/common/image.h2
-rw-r--r--src/apps/common/options.cpp12
-rw-r--r--src/apps/common/options.h2
-rw-r--r--src/apps/common/ppm_writer.cpp9
-rw-r--r--src/apps/common/ppm_writer.h2
-rw-r--r--src/apps/common/stream_options.cpp2
-rw-r--r--src/apps/common/stream_options.h2
-rw-r--r--src/apps/ipa-verify/main.cpp2
-rw-r--r--src/apps/lc-compliance/environment.cpp2
-rw-r--r--src/apps/lc-compliance/environment.h4
-rw-r--r--src/apps/lc-compliance/helpers/capture.cpp164
-rw-r--r--src/apps/lc-compliance/helpers/capture.h53
-rw-r--r--src/apps/lc-compliance/main.cpp44
-rw-r--r--src/apps/lc-compliance/tests/capture_test.cpp23
-rw-r--r--src/apps/qcam/assets/shader/bayer_8.vert4
-rw-r--r--src/apps/qcam/assets/shader/identity.vert3
-rw-r--r--src/apps/qcam/cam_select_dialog.cpp12
-rw-r--r--src/apps/qcam/cam_select_dialog.h2
-rw-r--r--src/apps/qcam/format_converter.cpp4
-rw-r--r--src/apps/qcam/format_converter.h2
-rw-r--r--src/apps/qcam/main.cpp6
-rw-r--r--src/apps/qcam/main_window.cpp69
-rw-r--r--src/apps/qcam/main_window.h4
-rw-r--r--src/apps/qcam/meson.build49
-rw-r--r--src/apps/qcam/message_handler.cpp2
-rw-r--r--src/apps/qcam/message_handler.h2
-rw-r--r--src/apps/qcam/viewfinder.h2
-rw-r--r--src/apps/qcam/viewfinder_gl.cpp54
-rw-r--r--src/apps/qcam/viewfinder_gl.h5
-rw-r--r--src/apps/qcam/viewfinder_qt.cpp53
-rw-r--r--src/apps/qcam/viewfinder_qt.h4
58 files changed, 657 insertions, 457 deletions
diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp
index 8447f932..9e934827 100644
--- a/src/apps/cam/camera_session.cpp
+++ b/src/apps/cam/camera_session.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_session.cpp - Camera capture session
+ * Camera capture session
*/
#include <iomanip>
@@ -39,9 +39,14 @@ CameraSession::CameraSession(CameraManager *cm,
{
char *endptr;
unsigned long index = strtoul(cameraId.c_str(), &endptr, 10);
- if (*endptr == '\0' && index > 0 && index <= cm->cameras().size())
- camera_ = cm->cameras()[index - 1];
- else
+
+ if (*endptr == '\0' && index > 0) {
+ auto cameras = cm->cameras();
+ if (index <= cameras.size())
+ camera_ = cameras[index - 1];
+ }
+
+ if (!camera_)
camera_ = cm->get(cameraId);
if (!camera_) {
@@ -154,8 +159,43 @@ CameraSession::~CameraSession()
void CameraSession::listControls() const
{
for (const auto &[id, info] : camera_->controls()) {
- std::cout << "Control: " << id->name() << ": "
- << info.toString() << std::endl;
+ std::stringstream io;
+ io << "["
+ << (id->isInput() ? "in" : " ")
+ << (id->isOutput() ? "out" : " ")
+ << "] ";
+
+ if (info.values().empty()) {
+ std::cout << "Control: " << io.str()
+ << id->vendor() << "::" << id->name() << ": "
+ << info.toString() << std::endl;
+ } else {
+ std::cout << "Control: " << io.str()
+ << id->vendor() << "::" << id->name() << ":"
+ << std::endl;
+ for (const auto &value : info.values()) {
+ int32_t val = value.get<int32_t>();
+ const auto &it = id->enumerators().find(val);
+
+ std::cout << " - ";
+ if (it == id->enumerators().end())
+ std::cout << "UNKNOWN";
+ else
+ std::cout << it->second;
+ std::cout << " (" << val << ")" << std::endl;
+ }
+ }
+
+ if (id->isArray()) {
+ std::size_t size = id->size();
+
+ std::cout << " Size: ";
+ if (size == std::numeric_limits<std::size_t>::max())
+ std::cout << "n";
+ else
+ std::cout << std::to_string(size);
+ std::cout << std::endl;
+ }
}
}
@@ -225,11 +265,16 @@ int CameraSession::start()
#endif
if (options_.isSet(OptFile)) {
- if (!options_[OptFile].toString().empty())
- sink_ = std::make_unique<FileSink>(camera_.get(), streamNames_,
- options_[OptFile]);
- else
- sink_ = std::make_unique<FileSink>(camera_.get(), streamNames_);
+ std::unique_ptr<FileSink> sink =
+ std::make_unique<FileSink>(camera_.get(), streamNames_);
+
+ if (!options_[OptFile].toString().empty()) {
+ ret = sink->setFilePattern(options_[OptFile]);
+ if (ret)
+ return ret;
+ }
+
+ sink_ = std::move(sink);
}
if (sink_) {
@@ -377,7 +422,7 @@ void CameraSession::requestComplete(Request *request)
* Defer processing of the completed request to the event loop, to avoid
* blocking the camera manager thread.
*/
- EventLoop::instance()->callLater([=]() { processRequest(request); });
+ EventLoop::instance()->callLater([this, request]() { processRequest(request); });
}
void CameraSession::processRequest(Request *request)
diff --git a/src/apps/cam/camera_session.h b/src/apps/cam/camera_session.h
index 0bab519f..4442fd9b 100644
--- a/src/apps/cam/camera_session.h
+++ b/src/apps/cam/camera_session.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_session.h - Camera capture session
+ * Camera capture session
*/
#pragma once
diff --git a/src/apps/cam/capture_script.cpp b/src/apps/cam/capture_script.cpp
index 1215713f..fc1dfa75 100644
--- a/src/apps/cam/capture_script.cpp
+++ b/src/apps/cam/capture_script.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * capture_script.cpp - Capture session configuration script
+ * Capture session configuration script
*/
#include "capture_script.h"
diff --git a/src/apps/cam/capture_script.h b/src/apps/cam/capture_script.h
index 40042c03..294b9203 100644
--- a/src/apps/cam/capture_script.h
+++ b/src/apps/cam/capture_script.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * capture_script.h - Capture session configuration script
+ * Capture session configuration script
*/
#pragma once
diff --git a/src/apps/cam/drm.cpp b/src/apps/cam/drm.cpp
index 8779a713..47bbb6b0 100644
--- a/src/apps/cam/drm.cpp
+++ b/src/apps/cam/drm.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * drm.cpp - DRM/KMS Helpers
+ * DRM/KMS Helpers
*/
#include "drm.h"
diff --git a/src/apps/cam/drm.h b/src/apps/cam/drm.h
index ebaea04d..1ba83b6e 100644
--- a/src/apps/cam/drm.h
+++ b/src/apps/cam/drm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * drm.h - DRM/KMS Helpers
+ * DRM/KMS Helpers
*/
#pragma once
diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp
index 906b50e6..76e21db9 100644
--- a/src/apps/cam/file_sink.cpp
+++ b/src/apps/cam/file_sink.cpp
@@ -2,9 +2,10 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * file_sink.cpp - File Sink
+ * File Sink
*/
+#include <array>
#include <assert.h>
#include <fcntl.h>
#include <iomanip>
@@ -12,6 +13,7 @@
#include <sstream>
#include <string.h>
#include <unistd.h>
+#include <utility>
#include <libcamera/camera.h>
@@ -24,13 +26,13 @@
using namespace libcamera;
FileSink::FileSink([[maybe_unused]] const libcamera::Camera *camera,
- const std::map<const libcamera::Stream *, std::string> &streamNames,
- const std::string &pattern)
+ const std::map<const libcamera::Stream *, std::string> &streamNames)
:
#ifdef HAVE_TIFF
camera_(camera),
#endif
- streamNames_(streamNames), pattern_(pattern)
+ pattern_(kDefaultFilePattern), fileType_(FileType::Binary),
+ streamNames_(streamNames)
{
}
@@ -38,6 +40,41 @@ FileSink::~FileSink()
{
}
+int FileSink::setFilePattern(const std::string &pattern)
+{
+ static const std::array<std::pair<std::string, FileType>, 2> types{{
+ { ".dng", FileType::Dng },
+ { ".ppm", FileType::Ppm },
+ }};
+
+ pattern_ = pattern;
+
+ if (pattern_.empty() || pattern_.back() == '/')
+ pattern_ += kDefaultFilePattern;
+
+ fileType_ = FileType::Binary;
+
+ for (const auto &type : types) {
+ if (pattern_.size() < type.first.size())
+ continue;
+
+ if (pattern_.find(type.first, pattern_.size() - type.first.size()) !=
+ std::string::npos) {
+ fileType_ = type.second;
+ break;
+ }
+ }
+
+#ifndef HAVE_TIFF
+ if (fileType_ == FileType::Dng) {
+ std::cerr << "DNG support not available" << std::endl;
+ return -EINVAL;
+ }
+#endif /* HAVE_TIFF */
+
+ return 0;
+}
+
int FileSink::configure(const libcamera::CameraConfiguration &config)
{
int ret = FrameSink::configure(config);
@@ -67,21 +104,10 @@ bool FileSink::processRequest(Request *request)
void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
[[maybe_unused]] const ControlList &metadata)
{
- std::string filename;
+ std::string filename = pattern_;
size_t pos;
int fd, ret = 0;
- if (!pattern_.empty())
- filename = pattern_;
-
-#ifdef HAVE_TIFF
- bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos;
-#endif /* HAVE_TIFF */
- bool ppm = filename.find(".ppm", filename.size() - 4) != std::string::npos;
-
- if (filename.empty() || filename.back() == '/')
- filename += "frame-#.bin";
-
pos = filename.find_first_of('#');
if (pos != std::string::npos) {
std::stringstream ss;
@@ -93,7 +119,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
Image *image = mappedBuffers_[buffer].get();
#ifdef HAVE_TIFF
- if (dng) {
+ if (fileType_ == FileType::Dng) {
ret = DNGWriter::write(filename.c_str(), camera_,
stream->configuration(), metadata,
buffer, image->data(0).data());
@@ -104,7 +130,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
return;
}
#endif /* HAVE_TIFF */
- if (ppm) {
+ if (fileType_ == FileType::Ppm) {
ret = PPMWriter::write(filename.c_str(), stream->configuration(),
image->data(0));
if (ret < 0)
diff --git a/src/apps/cam/file_sink.h b/src/apps/cam/file_sink.h
index 300edf8d..71b7fe0f 100644
--- a/src/apps/cam/file_sink.h
+++ b/src/apps/cam/file_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * file_sink.h - File Sink
+ * File Sink
*/
#pragma once
@@ -21,10 +21,11 @@ class FileSink : public FrameSink
{
public:
FileSink(const libcamera::Camera *camera,
- const std::map<const libcamera::Stream *, std::string> &streamNames,
- const std::string &pattern = "");
+ const std::map<const libcamera::Stream *, std::string> &streamNames);
~FileSink();
+ int setFilePattern(const std::string &pattern);
+
int configure(const libcamera::CameraConfiguration &config) override;
void mapBuffer(libcamera::FrameBuffer *buffer) override;
@@ -32,6 +33,14 @@ public:
bool processRequest(libcamera::Request *request) override;
private:
+ static constexpr const char *kDefaultFilePattern = "frame-#.bin";
+
+ enum class FileType {
+ Binary,
+ Dng,
+ Ppm,
+ };
+
void writeBuffer(const libcamera::Stream *stream,
libcamera::FrameBuffer *buffer,
const libcamera::ControlList &metadata);
@@ -39,7 +48,10 @@ private:
#ifdef HAVE_TIFF
const libcamera::Camera *camera_;
#endif
- std::map<const libcamera::Stream *, std::string> streamNames_;
+
std::string pattern_;
+ FileType fileType_;
+
+ std::map<const libcamera::Stream *, std::string> streamNames_;
std::map<libcamera::FrameBuffer *, std::unique_ptr<Image>> mappedBuffers_;
};
diff --git a/src/apps/cam/frame_sink.cpp b/src/apps/cam/frame_sink.cpp
index af21d575..68d6f2c1 100644
--- a/src/apps/cam/frame_sink.cpp
+++ b/src/apps/cam/frame_sink.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * frame_sink.cpp - Base Frame Sink Class
+ * Base Frame Sink Class
*/
#include "frame_sink.h"
diff --git a/src/apps/cam/frame_sink.h b/src/apps/cam/frame_sink.h
index ca4347cb..11105c6c 100644
--- a/src/apps/cam/frame_sink.h
+++ b/src/apps/cam/frame_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * frame_sink.h - Base Frame Sink Class
+ * Base Frame Sink Class
*/
#pragma once
diff --git a/src/apps/cam/kms_sink.cpp b/src/apps/cam/kms_sink.cpp
index 6991c3fa..672c985a 100644
--- a/src/apps/cam/kms_sink.cpp
+++ b/src/apps/cam/kms_sink.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * kms_sink.cpp - KMS Sink
+ * KMS Sink
*/
#include "kms_sink.h"
diff --git a/src/apps/cam/kms_sink.h b/src/apps/cam/kms_sink.h
index e2c618a1..4b7b4c26 100644
--- a/src/apps/cam/kms_sink.h
+++ b/src/apps/cam/kms_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * kms_sink.h - KMS Sink
+ * KMS Sink
*/
#pragma once
diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp
index 1aabee01..460dbc81 100644
--- a/src/apps/cam/main.cpp
+++ b/src/apps/cam/main.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main.cpp - cam - The libcamera swiss army knife
+ * cam - The libcamera swiss army knife
*/
#include <atomic>
@@ -344,12 +344,16 @@ std::string CamApp::cameraName(const Camera *camera)
return name;
}
+namespace {
+
void signalHandler([[maybe_unused]] int signal)
{
std::cout << "Exiting" << std::endl;
CamApp::instance()->quit();
}
+} /* namespace */
+
int main(int argc, char **argv)
{
CamApp app;
diff --git a/src/apps/cam/main.h b/src/apps/cam/main.h
index 4aa959b3..64e6a20e 100644
--- a/src/apps/cam/main.h
+++ b/src/apps/cam/main.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main.h - Cam application
+ * Cam application
*/
#pragma once
diff --git a/src/apps/cam/sdl_sink.cpp b/src/apps/cam/sdl_sink.cpp
index a2f4abc1..8355dd5e 100644
--- a/src/apps/cam/sdl_sink.cpp
+++ b/src/apps/cam/sdl_sink.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_sink.h - SDL Sink
+ * SDL Sink
*/
#include "sdl_sink.h"
diff --git a/src/apps/cam/sdl_sink.h b/src/apps/cam/sdl_sink.h
index 6c19c663..18ec7fbe 100644
--- a/src/apps/cam/sdl_sink.h
+++ b/src/apps/cam/sdl_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_sink.h - SDL Sink
+ * SDL Sink
*/
#pragma once
diff --git a/src/apps/cam/sdl_texture.cpp b/src/apps/cam/sdl_texture.cpp
index e9040bc5..e52c4a3a 100644
--- a/src/apps/cam/sdl_texture.cpp
+++ b/src/apps/cam/sdl_texture.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture.cpp - SDL Texture
+ * SDL Texture
*/
#include "sdl_texture.h"
diff --git a/src/apps/cam/sdl_texture.h b/src/apps/cam/sdl_texture.h
index 3993dd46..990f83b6 100644
--- a/src/apps/cam/sdl_texture.h
+++ b/src/apps/cam/sdl_texture.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture.h - SDL Texture
+ * SDL Texture
*/
#pragma once
diff --git a/src/apps/cam/sdl_texture_mjpg.cpp b/src/apps/cam/sdl_texture_mjpg.cpp
index da958e03..cace18fc 100644
--- a/src/apps/cam/sdl_texture_mjpg.cpp
+++ b/src/apps/cam/sdl_texture_mjpg.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_mjpg.cpp - SDL Texture MJPG
+ * SDL Texture MJPG
*/
#include "sdl_texture_mjpg.h"
diff --git a/src/apps/cam/sdl_texture_mjpg.h b/src/apps/cam/sdl_texture_mjpg.h
index 814ca79a..37bed5f0 100644
--- a/src/apps/cam/sdl_texture_mjpg.h
+++ b/src/apps/cam/sdl_texture_mjpg.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_mjpg.h - SDL Texture MJPG
+ * SDL Texture MJPG
*/
#pragma once
diff --git a/src/apps/cam/sdl_texture_yuv.cpp b/src/apps/cam/sdl_texture_yuv.cpp
index b29c3b93..480d7a37 100644
--- a/src/apps/cam/sdl_texture_yuv.cpp
+++ b/src/apps/cam/sdl_texture_yuv.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_yuv.cpp - SDL YUV Textures
+ * SDL YUV Textures
*/
#include "sdl_texture_yuv.h"
diff --git a/src/apps/cam/sdl_texture_yuv.h b/src/apps/cam/sdl_texture_yuv.h
index 310e4e50..29c756e7 100644
--- a/src/apps/cam/sdl_texture_yuv.h
+++ b/src/apps/cam/sdl_texture_yuv.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_yuv.h - SDL YUV Textures
+ * SDL YUV Textures
*/
#pragma once
diff --git a/src/apps/common/dng_writer.cpp b/src/apps/common/dng_writer.cpp
index 82bc065a..ac461951 100644
--- a/src/apps/common/dng_writer.cpp
+++ b/src/apps/common/dng_writer.cpp
@@ -2,14 +2,16 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * dng_writer.cpp - DNG writer
+ * DNG writer
*/
#include "dng_writer.h"
#include <algorithm>
+#include <endian.h>
#include <iostream>
#include <map>
+#include <vector>
#include <tiffio.h>
@@ -126,7 +128,9 @@ struct Matrix3d {
float m[9];
};
-void packScanlineSBGGR8(void *output, const void *input, unsigned int width)
+namespace {
+
+void packScanlineRaw8(void *output, const void *input, unsigned int width)
{
const uint8_t *in = static_cast<const uint8_t *>(input);
uint8_t *out = static_cast<uint8_t *>(output);
@@ -134,7 +138,67 @@ void packScanlineSBGGR8(void *output, const void *input, unsigned int width)
std::copy(in, in + width, out);
}
-void packScanlineSBGGR10P(void *output, const void *input, unsigned int width)
+void packScanlineRaw10(void *output, const void *input, unsigned int width)
+{
+ const uint8_t *in = static_cast<const uint8_t *>(input);
+ uint8_t *out = static_cast<uint8_t *>(output);
+
+ for (unsigned int i = 0; i < width; i += 4) {
+ *out++ = in[1] << 6 | in[0] >> 2;
+ *out++ = in[0] << 6 | (in[3] & 0x03) << 4 | in[2] >> 4;
+ *out++ = in[2] << 4 | (in[5] & 0x03) << 2 | in[4] >> 6;
+ *out++ = in[4] << 2 | (in[7] & 0x03) << 0;
+ *out++ = in[6];
+ in += 8;
+ }
+}
+
+void packScanlineRaw12(void *output, const void *input, unsigned int width)
+{
+ const uint8_t *in = static_cast<const uint8_t *>(input);
+ uint8_t *out = static_cast<uint8_t *>(output);
+
+ for (unsigned int i = 0; i < width; i += 2) {
+ *out++ = in[1] << 4 | in[0] >> 4;
+ *out++ = in[0] << 4 | (in[3] & 0x0f);
+ *out++ = in[2];
+ in += 4;
+ }
+}
+
+void packScanlineRaw16(void *output, const void *input, unsigned int width)
+{
+ const uint16_t *in = static_cast<const uint16_t *>(input);
+ uint16_t *out = static_cast<uint16_t *>(output);
+
+ std::copy(in, in + width, out);
+}
+
+/* Thumbnail function for raw data with each pixel aligned to 16bit. */
+void thumbScanlineRaw(const FormatInfo &info, void *output, const void *input,
+ unsigned int width, unsigned int stride)
+{
+ const uint16_t *in = static_cast<const uint16_t *>(input);
+ const uint16_t *in2 = static_cast<const uint16_t *>(input) + stride / 2;
+ uint8_t *out = static_cast<uint8_t *>(output);
+
+ /* Shift down to 8. */
+ unsigned int shift = info.bitsPerSample - 8;
+
+ /* Simple averaging that produces greyscale RGB values. */
+ for (unsigned int x = 0; x < width; x++) {
+ uint16_t value = (le16toh(in[0]) + le16toh(in[1]) +
+ le16toh(in2[0]) + le16toh(in2[1])) >> 2;
+ value = value >> shift;
+ *out++ = value;
+ *out++ = value;
+ *out++ = value;
+ in += 16;
+ in2 += 16;
+ }
+}
+
+void packScanlineRaw10_CSI2P(void *output, const void *input, unsigned int width)
{
const uint8_t *in = static_cast<const uint8_t *>(input);
uint8_t *out = static_cast<uint8_t *>(output);
@@ -150,7 +214,7 @@ void packScanlineSBGGR10P(void *output, const void *input, unsigned int width)
}
}
-void packScanlineSBGGR12P(void *output, const void *input, unsigned int width)
+void packScanlineRaw12_CSI2P(void *output, const void *input, unsigned int width)
{
const uint8_t *in = static_cast<const uint8_t *>(input);
uint8_t *out = static_cast<uint8_t *>(output);
@@ -164,7 +228,7 @@ void packScanlineSBGGR12P(void *output, const void *input, unsigned int width)
}
}
-void thumbScanlineSBGGRxxP(const FormatInfo &info, void *output,
+void thumbScanlineRaw_CSI2P(const FormatInfo &info, void *output,
const void *input, unsigned int width,
unsigned int stride)
{
@@ -282,78 +346,150 @@ void thumbScanlineIPU3([[maybe_unused]] const FormatInfo &info, void *output,
}
}
-static const std::map<PixelFormat, FormatInfo> formatInfo = {
+const std::map<PixelFormat, FormatInfo> formatInfo = {
{ formats::SBGGR8, {
.bitsPerSample = 8,
.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGBRG8, {
.bitsPerSample = 8,
.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGRBG8, {
.bitsPerSample = 8,
.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SRGGB8, {
.bitsPerSample = 8,
.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
+ } },
+ { formats::SBGGR10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGBRG10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGRBG10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SRGGB10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SBGGR12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGBRG12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGRBG12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SRGGB12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SBGGR16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGBRG16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGRBG16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SRGGB16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
} },
{ formats::SBGGR10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGBRG10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGRBG10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SRGGB10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SBGGR12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGBRG12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGRBG12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SRGGB12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SBGGR10_IPU3, {
.bitsPerSample = 16,
@@ -381,6 +517,8 @@ static const std::map<PixelFormat, FormatInfo> formatInfo = {
} },
};
+} /* namespace */
+
int DNGWriter::write(const char *filename, const Camera *camera,
const StreamConfiguration &config,
const ControlList &metadata,
@@ -407,7 +545,7 @@ int DNGWriter::write(const char *filename, const Camera *camera,
* or a thumbnail scanline. The latter will always be much smaller than
* the former as we downscale by 16 in both directions.
*/
- uint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8];
+ std::vector<uint8_t> scanline((config.size.width * info->bitsPerSample + 7) / 8);
toff_t rawIFDOffset = 0;
toff_t exifIFDOffset = 0;
@@ -507,10 +645,10 @@ int DNGWriter::write(const char *filename, const Camera *camera,
/* Write the thumbnail. */
const uint8_t *row = static_cast<const uint8_t *>(data);
for (unsigned int y = 0; y < config.size.height / 16; y++) {
- info->thumbScanline(*info, &scanline, row,
+ info->thumbScanline(*info, scanline.data(), row,
config.size.width / 16, config.stride);
- if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {
+ if (TIFFWriteScanline(tif, scanline.data(), y, 0) != 1) {
std::cerr << "Failed to write thumbnail scanline"
<< std::endl;
TIFFClose(tif);
@@ -522,6 +660,23 @@ int DNGWriter::write(const char *filename, const Camera *camera,
TIFFWriteDirectory(tif);
+ /*
+ * Workaround for a bug introduced in libtiff version 4.5.1 and no fix
+ * released. In these versions the CFA* tags were missing in the field
+ * info.
+ * Introduced by: https://gitlab.com/libtiff/libtiff/-/commit/738e04099b13192bb1f654e74e9b5829313f3161
+ * Fixed by: https://gitlab.com/libtiff/libtiff/-/commit/49856998c3d82e65444b47bb4fb11b7830a0c2be
+ */
+ if (!TIFFFindField(tif, TIFFTAG_CFAREPEATPATTERNDIM, TIFF_ANY)) {
+ static const TIFFFieldInfo infos[] = {
+ { TIFFTAG_CFAREPEATPATTERNDIM, 2, 2, TIFF_SHORT, FIELD_CUSTOM,
+ 1, 0, const_cast<char *>("CFARepeatPatternDim") },
+ { TIFFTAG_CFAPATTERN, -1, -1, TIFF_BYTE, FIELD_CUSTOM,
+ 1, 1, const_cast<char *>("CFAPattern") },
+ };
+ TIFFMergeFieldInfo(tif, infos, 2);
+ }
+
/* Create a new IFD for the RAW image. */
const uint16_t cfaRepeatPatternDim[] = { 2, 2 };
const uint8_t cfaPlaneColor[] = {
@@ -593,9 +748,9 @@ int DNGWriter::write(const char *filename, const Camera *camera,
/* Write RAW content. */
row = static_cast<const uint8_t *>(data);
for (unsigned int y = 0; y < config.size.height; y++) {
- info->packScanline(&scanline, row, config.size.width);
+ info->packScanline(scanline.data(), row, config.size.width);
- if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {
+ if (TIFFWriteScanline(tif, scanline.data(), y, 0) != 1) {
std::cerr << "Failed to write RAW scanline"
<< std::endl;
TIFFClose(tif);
diff --git a/src/apps/common/dng_writer.h b/src/apps/common/dng_writer.h
index 38f38f62..aaa8a852 100644
--- a/src/apps/common/dng_writer.h
+++ b/src/apps/common/dng_writer.h
@@ -2,13 +2,12 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * dng_writer.h - DNG writer
+ * DNG writer
*/
#pragma once
#ifdef HAVE_TIFF
-#define HAVE_DNG
#include <libcamera/camera.h>
#include <libcamera/controls.h>
diff --git a/src/apps/common/event_loop.cpp b/src/apps/common/event_loop.cpp
index cb83845c..b6230f4b 100644
--- a/src/apps/common/event_loop.cpp
+++ b/src/apps/common/event_loop.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event_loop.cpp - cam - Event loop
+ * cam - Event loop
*/
#include "event_loop.h"
@@ -21,12 +21,35 @@ EventLoop::EventLoop()
evthread_use_pthreads();
base_ = event_base_new();
instance_ = this;
+
+ callsTrigger_ = event_new(base_, -1, EV_PERSIST, [](evutil_socket_t, short, void *closure) {
+ auto *self = static_cast<EventLoop *>(closure);
+
+ for (;;) {
+ std::function<void()> call;
+
+ {
+ std::lock_guard locker(self->lock_);
+ if (self->calls_.empty())
+ break;
+
+ call = std::move(self->calls_.front());
+ self->calls_.pop_front();
+ }
+
+ call();
+ }
+ }, this);
+ assert(callsTrigger_);
+ event_add(callsTrigger_, nullptr);
}
EventLoop::~EventLoop()
{
instance_ = nullptr;
+ event_free(callsTrigger_);
+
events_.clear();
event_base_free(base_);
libevent_global_shutdown();
@@ -50,20 +73,20 @@ void EventLoop::exit(int code)
event_base_loopbreak(base_);
}
-void EventLoop::callLater(const std::function<void()> &func)
+void EventLoop::callLater(std::function<void()> &&func)
{
{
std::unique_lock<std::mutex> locker(lock_);
- calls_.push_back(func);
+ calls_.push_back(std::move(func));
}
- event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr);
+ event_active(callsTrigger_, 0, 0);
}
void EventLoop::addFdEvent(int fd, EventType type,
- const std::function<void()> &callback)
+ std::function<void()> &&callback)
{
- std::unique_ptr<Event> event = std::make_unique<Event>(callback);
+ std::unique_ptr<Event> event = std::make_unique<Event>(std::move(callback));
short events = (type & Read ? EV_READ : 0)
| (type & Write ? EV_WRITE : 0)
| EV_PERSIST;
@@ -85,9 +108,9 @@ void EventLoop::addFdEvent(int fd, EventType type,
}
void EventLoop::addTimerEvent(const std::chrono::microseconds period,
- const std::function<void()> &callback)
+ std::function<void()> &&callback)
{
- std::unique_ptr<Event> event = std::make_unique<Event>(callback);
+ std::unique_ptr<Event> event = std::make_unique<Event>(std::move(callback));
event->event_ = event_new(base_, -1, EV_PERSIST, &EventLoop::Event::dispatch,
event.get());
if (!event->event_) {
@@ -108,31 +131,8 @@ void EventLoop::addTimerEvent(const std::chrono::microseconds period,
events_.push_back(std::move(event));
}
-void EventLoop::dispatchCallback([[maybe_unused]] evutil_socket_t fd,
- [[maybe_unused]] short flags, void *param)
-{
- EventLoop *loop = static_cast<EventLoop *>(param);
- loop->dispatchCall();
-}
-
-void EventLoop::dispatchCall()
-{
- std::function<void()> call;
-
- {
- std::unique_lock<std::mutex> locker(lock_);
- if (calls_.empty())
- return;
-
- call = calls_.front();
- calls_.pop_front();
- }
-
- call();
-}
-
-EventLoop::Event::Event(const std::function<void()> &callback)
- : callback_(callback), event_(nullptr)
+EventLoop::Event::Event(std::function<void()> &&callback)
+ : callback_(std::move(callback)), event_(nullptr)
{
}
diff --git a/src/apps/common/event_loop.h b/src/apps/common/event_loop.h
index ef79e8e5..d8b6df2f 100644
--- a/src/apps/common/event_loop.h
+++ b/src/apps/common/event_loop.h
@@ -2,17 +2,20 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event_loop.h - cam - Event loop
+ * cam - Event loop
*/
#pragma once
#include <chrono>
+#include <deque>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
+#include <libcamera/base/class.h>
+
#include <event2/util.h>
struct event_base;
@@ -33,18 +36,20 @@ public:
int exec();
void exit(int code = 0);
- void callLater(const std::function<void()> &func);
+ void callLater(std::function<void()> &&func);
void addFdEvent(int fd, EventType type,
- const std::function<void()> &handler);
+ std::function<void()> &&handler);
- using duration = std::chrono::steady_clock::duration;
void addTimerEvent(const std::chrono::microseconds period,
- const std::function<void()> &handler);
+ std::function<void()> &&handler);
private:
+ LIBCAMERA_DISABLE_COPY_AND_MOVE(EventLoop)
+
struct Event {
- Event(const std::function<void()> &callback);
+ Event(std::function<void()> &&callback);
+ LIBCAMERA_DISABLE_COPY_AND_MOVE(Event)
~Event();
static void dispatch(int fd, short events, void *arg);
@@ -58,11 +63,9 @@ private:
struct event_base *base_;
int exitCode_;
- std::list<std::function<void()>> calls_;
+ std::deque<std::function<void()>> calls_;
+ struct event *callsTrigger_ = nullptr;
+
std::list<std::unique_ptr<Event>> events_;
std::mutex lock_;
-
- static void dispatchCallback(evutil_socket_t fd, short flags,
- void *param);
- void dispatchCall();
};
diff --git a/src/apps/common/image.cpp b/src/apps/common/image.cpp
index fe2cc6da..a2a0f58f 100644
--- a/src/apps/common/image.cpp
+++ b/src/apps/common/image.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * image.cpp - Multi-planar image with access to pixel data
+ * Multi-planar image with access to pixel data
*/
#include "image.h"
diff --git a/src/apps/common/image.h b/src/apps/common/image.h
index 7953b177..e47e446b 100644
--- a/src/apps/common/image.h
+++ b/src/apps/common/image.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * image.h - Multi-planar image with access to pixel data
+ * Multi-planar image with access to pixel data
*/
#pragma once
diff --git a/src/apps/common/options.cpp b/src/apps/common/options.cpp
index 4f7e8691..cae193cc 100644
--- a/src/apps/common/options.cpp
+++ b/src/apps/common/options.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * options.cpp - cam - Options parsing
+ * cam - Options parsing
*/
#include <assert.h>
@@ -10,6 +10,7 @@
#include <iomanip>
#include <iostream>
#include <string.h>
+#include <vector>
#include "options.h"
@@ -879,8 +880,8 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)
* Allocate short and long options arrays large enough to contain all
* options.
*/
- char shortOptions[optionsMap_.size() * 3 + 2];
- struct option longOptions[optionsMap_.size() + 1];
+ std::vector<char> shortOptions(optionsMap_.size() * 3 + 2);
+ std::vector<struct option> longOptions(optionsMap_.size() + 1);
unsigned int ids = 0;
unsigned int idl = 0;
@@ -922,7 +923,8 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)
opterr = 0;
while (true) {
- int c = getopt_long(argc, argv, shortOptions, longOptions, nullptr);
+ int c = getopt_long(argc, argv, shortOptions.data(),
+ longOptions.data(), nullptr);
if (c == -1)
break;
@@ -1038,7 +1040,7 @@ void OptionsParser::usageOptions(const std::list<Option> &options,
std::cerr << std::setw(indent) << argument;
- for (const char *help = option.help, *end = help; end; ) {
+ for (const char *help = option.help, *end = help; end;) {
end = strchr(help, '\n');
if (end) {
std::cerr << std::string(help, end - help + 1);
diff --git a/src/apps/common/options.h b/src/apps/common/options.h
index 4ddd4987..9771aa7a 100644
--- a/src/apps/common/options.h
+++ b/src/apps/common/options.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * options.h - cam - Options parsing
+ * cam - Options parsing
*/
#pragma once
diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp
index a8ccf67a..368de8bf 100644
--- a/src/apps/common/ppm_writer.cpp
+++ b/src/apps/common/ppm_writer.cpp
@@ -2,11 +2,12 @@
/*
* Copyright (C) 2024 Red Hat, Inc.
*
- * ppm_writer.cpp - PPM writer
+ * PPM writer
*/
#include "ppm_writer.h"
+#include <errno.h>
#include <fstream>
#include <iostream>
@@ -28,7 +29,7 @@ int PPMWriter::write(const char *filename,
std::ofstream output(filename, std::ios::binary);
if (!output) {
std::cerr << "Failed to open ppm file: " << filename << std::endl;
- return -EINVAL;
+ return -EIO;
}
output << "P6" << std::endl
@@ -36,7 +37,7 @@ int PPMWriter::write(const char *filename,
<< "255" << std::endl;
if (!output) {
std::cerr << "Failed to write the file header" << std::endl;
- return -EINVAL;
+ return -EIO;
}
const unsigned int rowLength = config.size.width * 3;
@@ -45,7 +46,7 @@ int PPMWriter::write(const char *filename,
output.write(row, rowLength);
if (!output) {
std::cerr << "Failed to write image data at row " << y << std::endl;
- return -EINVAL;
+ return -EIO;
}
}
diff --git a/src/apps/common/ppm_writer.h b/src/apps/common/ppm_writer.h
index 4c38f5ce..8c8d2e15 100644
--- a/src/apps/common/ppm_writer.h
+++ b/src/apps/common/ppm_writer.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2024, Red Hat, Inc.
*
- * ppm_writer.h - PPM writer
+ * PPM writer
*/
#pragma once
diff --git a/src/apps/common/stream_options.cpp b/src/apps/common/stream_options.cpp
index 663b979a..99239e07 100644
--- a/src/apps/common/stream_options.cpp
+++ b/src/apps/common/stream_options.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * stream_options.cpp - Helper to parse options for streams
+ * Helper to parse options for streams
*/
#include "stream_options.h"
diff --git a/src/apps/common/stream_options.h b/src/apps/common/stream_options.h
index a5f3bde0..a93f104c 100644
--- a/src/apps/common/stream_options.h
+++ b/src/apps/common/stream_options.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * stream_options.h - Helper to parse options for streams
+ * Helper to parse options for streams
*/
#pragma once
diff --git a/src/apps/ipa-verify/main.cpp b/src/apps/ipa-verify/main.cpp
index 76ba5073..0903cd85 100644
--- a/src/apps/ipa-verify/main.cpp
+++ b/src/apps/ipa-verify/main.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Ideas on Board Oy
*
- * ipa_verify.cpp - Verify signature on an IPA module
+ * Verify signature on an IPA module
*/
#include <iostream>
diff --git a/src/apps/lc-compliance/environment.cpp b/src/apps/lc-compliance/environment.cpp
index 5eb3775f..987264f1 100644
--- a/src/apps/lc-compliance/environment.cpp
+++ b/src/apps/lc-compliance/environment.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Collabora Ltd.
*
- * environment.cpp - Common environment for tests
+ * Common environment for tests
*/
#include "environment.h"
diff --git a/src/apps/lc-compliance/environment.h b/src/apps/lc-compliance/environment.h
index 0debbcce..834c722e 100644
--- a/src/apps/lc-compliance/environment.h
+++ b/src/apps/lc-compliance/environment.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Collabora Ltd.
*
- * environment.h - Common environment for tests
+ * Common environment for tests
*/
#pragma once
@@ -23,5 +23,5 @@ private:
Environment() = default;
std::string cameraId_;
- libcamera::CameraManager *cm_;
+ libcamera::CameraManager *cm_ = nullptr;
};
diff --git a/src/apps/lc-compliance/helpers/capture.cpp b/src/apps/lc-compliance/helpers/capture.cpp
index 5aab973f..f2c6d58c 100644
--- a/src/apps/lc-compliance/helpers/capture.cpp
+++ b/src/apps/lc-compliance/helpers/capture.cpp
@@ -2,18 +2,19 @@
/*
* Copyright (C) 2020-2021, Google Inc.
*
- * simple_capture.cpp - Simple capture helper
+ * Simple capture helper
*/
#include "capture.h"
+#include <assert.h>
+
#include <gtest/gtest.h>
using namespace libcamera;
Capture::Capture(std::shared_ptr<Camera> camera)
- : loop_(nullptr), camera_(camera),
- allocator_(std::make_unique<FrameBufferAllocator>(camera))
+ : camera_(std::move(camera)), allocator_(camera_)
{
}
@@ -26,10 +27,8 @@ void Capture::configure(StreamRole role)
{
config_ = camera_->generateConfiguration({ role });
- if (!config_) {
- std::cout << "Role not supported by camera" << std::endl;
- GTEST_SKIP();
- }
+ if (!config_)
+ GTEST_SKIP() << "Role not supported by camera";
if (config_->validate() != CameraConfiguration::Valid) {
config_.reset();
@@ -42,155 +41,106 @@ void Capture::configure(StreamRole role)
}
}
-void Capture::start()
+void Capture::run(unsigned int captureLimit, std::optional<unsigned int> queueLimit)
{
- Stream *stream = config_->at(0).stream();
- int count = allocator_->allocate(stream);
-
- ASSERT_GE(count, 0) << "Failed to allocate buffers";
- EXPECT_EQ(count, config_->at(0).bufferCount) << "Allocated less buffers than expected";
-
- camera_->requestCompleted.connect(this, &Capture::requestComplete);
-
- ASSERT_EQ(camera_->start(), 0) << "Failed to start camera";
-}
+ assert(!queueLimit || captureLimit <= *queueLimit);
-void Capture::stop()
-{
- if (!config_ || !allocator_->allocated())
- return;
+ captureLimit_ = captureLimit;
+ queueLimit_ = queueLimit;
- camera_->stop();
+ captureCount_ = queueCount_ = 0;
- camera_->requestCompleted.disconnect(this);
+ EventLoop loop;
+ loop_ = &loop;
- Stream *stream = config_->at(0).stream();
- requests_.clear();
- allocator_->free(stream);
-}
-
-/* CaptureBalanced */
-
-CaptureBalanced::CaptureBalanced(std::shared_ptr<Camera> camera)
- : Capture(camera)
-{
-}
-
-void CaptureBalanced::capture(unsigned int numRequests)
-{
start();
- Stream *stream = config_->at(0).stream();
- const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);
-
- /* No point in testing less requests then the camera depth. */
- if (buffers.size() > numRequests) {
- std::cout << "Camera needs " + std::to_string(buffers.size())
- + " requests, can't test only "
- + std::to_string(numRequests) << std::endl;
- GTEST_SKIP();
- }
+ for (const auto &request : requests_)
+ queueRequest(request.get());
- queueCount_ = 0;
- captureCount_ = 0;
- captureLimit_ = numRequests;
+ EXPECT_EQ(loop_->exec(), 0);
- /* Queue the recommended number of requests. */
- for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
- std::unique_ptr<Request> request = camera_->createRequest();
- ASSERT_TRUE(request) << "Can't create request";
-
- ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request";
-
- ASSERT_EQ(queueRequest(request.get()), 0) << "Failed to queue request";
-
- requests_.push_back(std::move(request));
- }
-
- /* Run capture session. */
- loop_ = new EventLoop();
- loop_->exec();
stop();
- delete loop_;
- ASSERT_EQ(captureCount_, captureLimit_);
+ EXPECT_LE(captureLimit_, captureCount_);
+ EXPECT_LE(captureCount_, queueCount_);
+ EXPECT_TRUE(!queueLimit_ || queueCount_ <= *queueLimit_);
}
-int CaptureBalanced::queueRequest(Request *request)
+int Capture::queueRequest(libcamera::Request *request)
{
- queueCount_++;
- if (queueCount_ > captureLimit_)
+ if (queueLimit_ && queueCount_ >= *queueLimit_)
return 0;
- return camera_->queueRequest(request);
+ int ret = camera_->queueRequest(request);
+ if (ret < 0)
+ return ret;
+
+ queueCount_ += 1;
+ return 0;
}
-void CaptureBalanced::requestComplete(Request *request)
+void Capture::requestComplete(Request *request)
{
- EXPECT_EQ(request->status(), Request::Status::RequestComplete)
- << "Request didn't complete successfully";
-
captureCount_++;
if (captureCount_ >= captureLimit_) {
loop_->exit(0);
return;
}
+ EXPECT_EQ(request->status(), Request::Status::RequestComplete)
+ << "Request didn't complete successfully";
+
request->reuse(Request::ReuseBuffers);
if (queueRequest(request))
loop_->exit(-EINVAL);
}
-/* CaptureUnbalanced */
-
-CaptureUnbalanced::CaptureUnbalanced(std::shared_ptr<Camera> camera)
- : Capture(camera)
-{
-}
-
-void CaptureUnbalanced::capture(unsigned int numRequests)
+void Capture::start()
{
- start();
+ assert(config_);
+ assert(!config_->empty());
+ assert(!allocator_.allocated());
+ assert(requests_.empty());
Stream *stream = config_->at(0).stream();
- const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);
+ int count = allocator_.allocate(stream);
- captureCount_ = 0;
- captureLimit_ = numRequests;
+ ASSERT_GE(count, 0) << "Failed to allocate buffers";
+ EXPECT_EQ(count, config_->at(0).bufferCount) << "Allocated less buffers than expected";
+
+ const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_.buffers(stream);
+
+ /* No point in testing less requests then the camera depth. */
+ if (queueLimit_ && *queueLimit_ < buffers.size()) {
+ GTEST_SKIP() << "Camera needs " << buffers.size()
+ << " requests, can't test only " << *queueLimit_;
+ }
- /* Queue the recommended number of requests. */
for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
std::unique_ptr<Request> request = camera_->createRequest();
ASSERT_TRUE(request) << "Can't create request";
ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request";
- ASSERT_EQ(camera_->queueRequest(request.get()), 0) << "Failed to queue request";
-
requests_.push_back(std::move(request));
}
- /* Run capture session. */
- loop_ = new EventLoop();
- int status = loop_->exec();
- stop();
- delete loop_;
+ camera_->requestCompleted.connect(this, &Capture::requestComplete);
- ASSERT_EQ(status, 0);
+ ASSERT_EQ(camera_->start(), 0) << "Failed to start camera";
}
-void CaptureUnbalanced::requestComplete(Request *request)
+void Capture::stop()
{
- captureCount_++;
- if (captureCount_ >= captureLimit_) {
- loop_->exit(0);
+ if (!config_ || !allocator_.allocated())
return;
- }
- EXPECT_EQ(request->status(), Request::Status::RequestComplete)
- << "Request didn't complete successfully";
+ camera_->stop();
- request->reuse(Request::ReuseBuffers);
- if (camera_->queueRequest(request))
- loop_->exit(-EINVAL);
+ camera_->requestCompleted.disconnect(this);
+
+ Stream *stream = config_->at(0).stream();
+ requests_.clear();
+ allocator_.free(stream);
}
diff --git a/src/apps/lc-compliance/helpers/capture.h b/src/apps/lc-compliance/helpers/capture.h
index 0574ab1c..0e7b848f 100644
--- a/src/apps/lc-compliance/helpers/capture.h
+++ b/src/apps/lc-compliance/helpers/capture.h
@@ -2,12 +2,13 @@
/*
* Copyright (C) 2020-2021, Google Inc.
*
- * simple_capture.h - Simple capture helper
+ * Simple capture helper
*/
#pragma once
#include <memory>
+#include <optional>
#include <libcamera/libcamera.h>
@@ -16,51 +17,29 @@
class Capture
{
public:
+ Capture(std::shared_ptr<libcamera::Camera> camera);
+ ~Capture();
+
void configure(libcamera::StreamRole role);
+ void run(unsigned int captureLimit, std::optional<unsigned int> queueLimit = {});
-protected:
- Capture(std::shared_ptr<libcamera::Camera> camera);
- virtual ~Capture();
+private:
+ LIBCAMERA_DISABLE_COPY_AND_MOVE(Capture)
void start();
void stop();
- virtual void requestComplete(libcamera::Request *request) = 0;
-
- EventLoop *loop_;
+ int queueRequest(libcamera::Request *request);
+ void requestComplete(libcamera::Request *request);
std::shared_ptr<libcamera::Camera> camera_;
- std::unique_ptr<libcamera::FrameBufferAllocator> allocator_;
+ libcamera::FrameBufferAllocator allocator_;
std::unique_ptr<libcamera::CameraConfiguration> config_;
std::vector<std::unique_ptr<libcamera::Request>> requests_;
-};
-
-class CaptureBalanced : public Capture
-{
-public:
- CaptureBalanced(std::shared_ptr<libcamera::Camera> camera);
-
- void capture(unsigned int numRequests);
-
-private:
- int queueRequest(libcamera::Request *request);
- void requestComplete(libcamera::Request *request) override;
-
- unsigned int queueCount_;
- unsigned int captureCount_;
- unsigned int captureLimit_;
-};
-
-class CaptureUnbalanced : public Capture
-{
-public:
- CaptureUnbalanced(std::shared_ptr<libcamera::Camera> camera);
-
- void capture(unsigned int numRequests);
-
-private:
- void requestComplete(libcamera::Request *request) override;
- unsigned int captureCount_;
- unsigned int captureLimit_;
+ EventLoop *loop_ = nullptr;
+ unsigned int captureLimit_ = 0;
+ std::optional<unsigned int> queueLimit_;
+ unsigned int captureCount_ = 0;
+ unsigned int queueCount_ = 0;
};
diff --git a/src/apps/lc-compliance/main.cpp b/src/apps/lc-compliance/main.cpp
index 74e0d4df..e9f0ffbb 100644
--- a/src/apps/lc-compliance/main.cpp
+++ b/src/apps/lc-compliance/main.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Google Inc.
* Copyright (C) 2021, Collabora Ltd.
*
- * main.cpp - lc-compliance - The libcamera compliance tool
+ * lc-compliance - The libcamera compliance tool
*/
#include <iomanip>
@@ -45,13 +45,11 @@ class ThrowListener : public testing::EmptyTestEventListener
static void listCameras(CameraManager *cm)
{
for (const std::shared_ptr<Camera> &cam : cm->cameras())
- std::cout << "- " << cam.get()->id() << std::endl;
+ std::cout << "- " << cam->id() << std::endl;
}
static int initCamera(CameraManager *cm, OptionsParser::Options options)
{
- std::shared_ptr<Camera> camera;
-
int ret = cm->start();
if (ret) {
std::cout << "Failed to start camera manager: "
@@ -66,7 +64,7 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options)
}
const std::string &cameraId = options[OptCamera];
- camera = cm->get(cameraId);
+ std::shared_ptr<Camera> camera = cm->get(cameraId);
if (!camera) {
std::cout << "Camera " << cameraId << " not found, available cameras:" << std::endl;
listCameras(cm);
@@ -82,45 +80,27 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options)
static int initGtestParameters(char *arg0, OptionsParser::Options options)
{
- const std::map<std::string, std::string> gtestFlags = { { "list", "--gtest_list_tests" },
- { "filter", "--gtest_filter" } };
-
- int argc = 0;
+ std::vector<const char *> argv;
std::string filterParam;
- /*
- * +2 to have space for both the 0th argument that is needed but not
- * used and the null at the end.
- */
- char **argv = new char *[(gtestFlags.size() + 2)];
- if (!argv)
- return -ENOMEM;
-
- argv[0] = arg0;
- argc++;
+ argv.push_back(arg0);
- if (options.isSet(OptList)) {
- argv[argc] = const_cast<char *>(gtestFlags.at("list").c_str());
- argc++;
- }
+ if (options.isSet(OptList))
+ argv.push_back("--gtest_list_tests");
if (options.isSet(OptFilter)) {
/*
* The filter flag needs to be passed as a single parameter, in
* the format --gtest_filter=filterStr
*/
- filterParam = gtestFlags.at("filter") + "=" +
- static_cast<const std::string &>(options[OptFilter]);
-
- argv[argc] = const_cast<char *>(filterParam.c_str());
- argc++;
+ filterParam = "--gtest_filter=" + options[OptFilter].toString();
+ argv.push_back(filterParam.c_str());
}
- argv[argc] = nullptr;
-
- ::testing::InitGoogleTest(&argc, argv);
+ argv.push_back(nullptr);
- delete[] argv;
+ int argc = argv.size();
+ ::testing::InitGoogleTest(&argc, const_cast<char **>(argv.data()));
return 0;
}
diff --git a/src/apps/lc-compliance/tests/capture_test.cpp b/src/apps/lc-compliance/tests/capture_test.cpp
index 284d3630..93bed48f 100644
--- a/src/apps/lc-compliance/tests/capture_test.cpp
+++ b/src/apps/lc-compliance/tests/capture_test.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Google Inc.
* Copyright (C) 2021, Collabora Ltd.
*
- * capture_test.cpp - Test camera capture
+ * Test camera capture
*/
#include "capture.h"
@@ -14,10 +14,13 @@
#include "environment.h"
+namespace {
+
using namespace libcamera;
-const std::vector<int> NUMREQUESTS = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
-const std::vector<StreamRole> ROLES = {
+const int NUMREQUESTS[] = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
+
+const StreamRole ROLES[] = {
StreamRole::Raw,
StreamRole::StillCapture,
StreamRole::VideoRecording,
@@ -84,11 +87,11 @@ TEST_P(SingleStream, Capture)
{
auto [role, numRequests] = GetParam();
- CaptureBalanced capture(camera_);
+ Capture capture(camera_);
capture.configure(role);
- capture.capture(numRequests);
+ capture.run(numRequests, numRequests);
}
/*
@@ -103,12 +106,12 @@ TEST_P(SingleStream, CaptureStartStop)
auto [role, numRequests] = GetParam();
unsigned int numRepeats = 3;
- CaptureBalanced capture(camera_);
+ Capture capture(camera_);
capture.configure(role);
for (unsigned int starts = 0; starts < numRepeats; starts++)
- capture.capture(numRequests);
+ capture.run(numRequests, numRequests);
}
/*
@@ -122,11 +125,11 @@ TEST_P(SingleStream, UnbalancedStop)
{
auto [role, numRequests] = GetParam();
- CaptureUnbalanced capture(camera_);
+ Capture capture(camera_);
capture.configure(role);
- capture.capture(numRequests);
+ capture.run(numRequests);
}
INSTANTIATE_TEST_SUITE_P(CaptureTests,
@@ -134,3 +137,5 @@ INSTANTIATE_TEST_SUITE_P(CaptureTests,
testing::Combine(testing::ValuesIn(ROLES),
testing::ValuesIn(NUMREQUESTS)),
SingleStream::nameParameters);
+
+} /* namespace */
diff --git a/src/apps/qcam/assets/shader/bayer_8.vert b/src/apps/qcam/assets/shader/bayer_8.vert
index 3695a5e9..fb5109ee 100644
--- a/src/apps/qcam/assets/shader/bayer_8.vert
+++ b/src/apps/qcam/assets/shader/bayer_8.vert
@@ -19,6 +19,8 @@ Copyright (C) 2021, Linaro
attribute vec4 vertexIn;
attribute vec2 textureIn;
+uniform mat4 proj_matrix;
+
uniform vec2 tex_size; /* The texture size in pixels */
uniform vec2 tex_step;
@@ -47,5 +49,5 @@ void main(void) {
yCoord = center.y + vec4(-2.0 * tex_step.y,
-tex_step.y, tex_step.y, 2.0 * tex_step.y);
- gl_Position = vertexIn;
+ gl_Position = proj_matrix * vertexIn;
}
diff --git a/src/apps/qcam/assets/shader/identity.vert b/src/apps/qcam/assets/shader/identity.vert
index 12c41377..907e8741 100644
--- a/src/apps/qcam/assets/shader/identity.vert
+++ b/src/apps/qcam/assets/shader/identity.vert
@@ -9,10 +9,11 @@ attribute vec4 vertexIn;
attribute vec2 textureIn;
varying vec2 textureOut;
+uniform mat4 proj_matrix;
uniform float stride_factor;
void main(void)
{
- gl_Position = vertexIn;
+ gl_Position = proj_matrix * vertexIn;
textureOut = vec2(textureIn.x * stride_factor, textureIn.y);
}
diff --git a/src/apps/qcam/cam_select_dialog.cpp b/src/apps/qcam/cam_select_dialog.cpp
index 3c8b12a9..6b6d0713 100644
--- a/src/apps/qcam/cam_select_dialog.cpp
+++ b/src/apps/qcam/cam_select_dialog.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Utkarsh Tiwari <utkarsh02t@gmail.com>
*
- * cam_select_dialog.cpp - qcam - Camera Selection dialog
+ * qcam - Camera Selection dialog
*/
#include "cam_select_dialog.h"
@@ -15,7 +15,9 @@
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFormLayout>
+#include <QGuiApplication>
#include <QLabel>
+#include <QScreen>
#include <QString>
CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManager,
@@ -53,6 +55,14 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag
layout->addRow("Location:", cameraLocation_);
layout->addRow("Model:", cameraModel_);
layout->addWidget(buttonBox);
+
+ /*
+ * Decrease the minimum width of dialog to fit on narrow screens, with a
+ * 20 pixels margin.
+ */
+ QRect screenGeometry = qGuiApp->primaryScreen()->availableGeometry();
+ if (screenGeometry.width() < minimumWidth())
+ setMinimumWidth(screenGeometry.width() - 20);
}
CameraSelectorDialog::~CameraSelectorDialog() = default;
diff --git a/src/apps/qcam/cam_select_dialog.h b/src/apps/qcam/cam_select_dialog.h
index 0b7709ed..4bec9ea9 100644
--- a/src/apps/qcam/cam_select_dialog.h
+++ b/src/apps/qcam/cam_select_dialog.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Utkarsh Tiwari <utkarsh02t@gmail.com>
*
- * cam_select_dialog.h - qcam - Camera Selection dialog
+ * qcam - Camera Selection dialog
*/
#pragma once
diff --git a/src/apps/qcam/format_converter.cpp b/src/apps/qcam/format_converter.cpp
index de76b32c..b025a3c7 100644
--- a/src/apps/qcam/format_converter.cpp
+++ b/src/apps/qcam/format_converter.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * format_convert.cpp - qcam - Convert buffer to RGB
+ * Convert buffer to RGB
*/
#include "format_converter.h"
@@ -249,7 +249,7 @@ void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst
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_; ) {
+ 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];
diff --git a/src/apps/qcam/format_converter.h b/src/apps/qcam/format_converter.h
index 37dbfae2..819a87a5 100644
--- a/src/apps/qcam/format_converter.h
+++ b/src/apps/qcam/format_converter.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * format_convert.h - qcam - Convert buffer to RGB
+ * Convert buffer to RGB
*/
#pragma once
diff --git a/src/apps/qcam/main.cpp b/src/apps/qcam/main.cpp
index 36cb93a5..d0bde141 100644
--- a/src/apps/qcam/main.cpp
+++ b/src/apps/qcam/main.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main.cpp - qcam - The libcamera GUI test application
+ * qcam - The libcamera GUI test application
*/
#include <signal.h>
@@ -21,6 +21,8 @@
using namespace libcamera;
+namespace {
+
void signalHandler([[maybe_unused]] int signal)
{
qInfo() << "Exiting";
@@ -52,6 +54,8 @@ OptionsParser::Options parseOptions(int argc, char *argv[])
return options;
}
+} /* namespace */
+
int main(int argc, char **argv)
{
QApplication app(argc, argv);
diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp
index 0f16c038..3880a846 100644
--- a/src/apps/qcam/main_window.cpp
+++ b/src/apps/qcam/main_window.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main_window.cpp - qcam - Main application window
+ * qcam - Main application window
*/
#include "main_window.h"
@@ -37,16 +37,6 @@
using namespace libcamera;
-#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
-/*
- * Qt::fixed was introduced in v5.14, and ::fixed deprecated in v5.15. Allow
- * usage of Qt::fixed unconditionally.
- */
-namespace Qt {
-constexpr auto fixed = ::fixed;
-} /* namespace Qt */
-#endif
-
/**
* \brief Custom QEvent to signal capture completion
*/
@@ -190,7 +180,7 @@ int MainWindow::createToolbars()
action = toolbar_->addAction(QIcon::fromTheme("application-exit",
QIcon(":x-circle.svg")),
"Quit");
- action->setShortcut(Qt::CTRL | Qt::Key_Q);
+ action->setShortcut(QKeySequence::Quit);
connect(action, &QAction::triggered, this, &MainWindow::quit);
/* Camera selector. */
@@ -221,7 +211,7 @@ int MainWindow::createToolbars()
action->setShortcut(QKeySequence::SaveAs);
connect(action, &QAction::triggered, this, &MainWindow::saveImageAs);
-#ifdef HAVE_DNG
+#ifdef HAVE_TIFF
/* Save Raw action. */
action = toolbar_->addAction(QIcon::fromTheme("camera-photo",
QIcon(":aperture.svg")),
@@ -261,16 +251,14 @@ void MainWindow::updateTitle()
void MainWindow::switchCamera()
{
/* Get and acquire the new camera. */
- std::string newCameraId = chooseCamera();
+ std::shared_ptr<Camera> cam = chooseCamera();
- if (newCameraId.empty())
+ if (!cam)
return;
- if (camera_ && newCameraId == camera_->id())
+ if (camera_ && cam == camera_)
return;
- const std::shared_ptr<Camera> &cam = cm_->get(newCameraId);
-
if (cam->acquire()) {
qInfo() << "Failed to acquire camera" << cam->id().c_str();
return;
@@ -292,40 +280,41 @@ void MainWindow::switchCamera()
startStopAction_->setChecked(true);
/* Display the current cameraId in the toolbar .*/
- cameraSelectButton_->setText(QString::fromStdString(newCameraId));
+ cameraSelectButton_->setText(QString::fromStdString(cam->id()));
}
-std::string MainWindow::chooseCamera()
+std::shared_ptr<Camera> MainWindow::chooseCamera()
{
if (cameraSelectorDialog_->exec() != QDialog::Accepted)
- return std::string();
+ return {};
- return cameraSelectorDialog_->getCameraId();
+ std::string id = cameraSelectorDialog_->getCameraId();
+ return cm_->get(id);
}
int MainWindow::openCamera()
{
- std::string cameraName;
-
/*
- * Use the camera specified on the command line, if any, or display the
- * camera selection dialog box otherwise.
+ * If a camera is specified on the command line, get it. Otherwise, if
+ * only one camera is available, pick it automatically, else, display
+ * the selector dialog box.
*/
- if (options_.isSet(OptCamera))
- cameraName = static_cast<std::string>(options_[OptCamera]);
- else
- cameraName = chooseCamera();
-
- if (cameraName == "")
- return -EINVAL;
+ if (options_.isSet(OptCamera)) {
+ std::string cameraName = static_cast<std::string>(options_[OptCamera]);
+ camera_ = cm_->get(cameraName);
+ if (!camera_)
+ qInfo() << "Camera" << cameraName.c_str() << "not found";
+ } else {
+ std::vector<std::shared_ptr<Camera>> cameras = cm_->cameras();
+ camera_ = (cameras.size() == 1) ? cameras[0] : chooseCamera();
+ if (!camera_)
+ qInfo() << "No camera detected";
+ }
- /* Get and acquire the camera. */
- camera_ = cm_->get(cameraName);
- if (!camera_) {
- qInfo() << "Camera" << cameraName.c_str() << "not found";
+ if (!camera_)
return -ENODEV;
- }
+ /* Acquire the camera. */
if (camera_->acquire()) {
qInfo() << "Failed to acquire camera";
camera_.reset();
@@ -333,7 +322,7 @@ int MainWindow::openCamera()
}
/* Set the camera switch button with the currently selected Camera id. */
- cameraSelectButton_->setText(QString::fromStdString(cameraName));
+ cameraSelectButton_->setText(QString::fromStdString(camera_->id()));
return 0;
}
@@ -656,7 +645,7 @@ void MainWindow::captureRaw()
void MainWindow::processRaw(FrameBuffer *buffer,
[[maybe_unused]] const ControlList &metadata)
{
-#ifdef HAVE_DNG
+#ifdef HAVE_TIFF
QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
QString filename = QFileDialog::getSaveFileName(this, "Save DNG", defaultPath,
"DNG Files (*.dng)");
diff --git a/src/apps/qcam/main_window.h b/src/apps/qcam/main_window.h
index 2e3e1b5c..81fcf915 100644
--- a/src/apps/qcam/main_window.h
+++ b/src/apps/qcam/main_window.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main_window.h - qcam - Main application window
+ * qcam - Main application window
*/
#pragma once
@@ -73,7 +73,7 @@ private Q_SLOTS:
private:
int createToolbars();
- std::string chooseCamera();
+ std::shared_ptr<libcamera::Camera> chooseCamera();
int openCamera();
int startCapture();
diff --git a/src/apps/qcam/meson.build b/src/apps/qcam/meson.build
index 6cf4c171..f7c14064 100644
--- a/src/apps/qcam/meson.build
+++ b/src/apps/qcam/meson.build
@@ -1,13 +1,13 @@
# SPDX-License-Identifier: CC0-1.0
-qt5 = import('qt5')
-qt5_dep = dependency('qt5',
+qt6 = import('qt6')
+qt6_dep = dependency('qt6',
method : 'pkg-config',
- modules : ['Core', 'Gui', 'Widgets'],
+ modules : ['Core', 'Gui', 'OpenGL', 'OpenGLWidgets', 'Widgets'],
required : get_option('qcam'),
- version : '>=5.4')
+ version : '>=6.2')
-if not qt5_dep.found()
+if not qt6_dep.found()
qcam_enabled = false
subdir_done()
endif
@@ -20,46 +20,31 @@ qcam_sources = files([
'main.cpp',
'main_window.cpp',
'message_handler.cpp',
+ 'viewfinder_gl.cpp',
'viewfinder_qt.cpp',
])
qcam_moc_headers = files([
'cam_select_dialog.h',
'main_window.h',
+ 'viewfinder_gl.h',
'viewfinder_qt.h',
])
qcam_resources = files([
'assets/feathericons/feathericons.qrc',
+ 'assets/shader/shaders.qrc',
])
-qt5_cpp_args = [apps_cpp_args, '-DQT_NO_KEYWORDS']
+qt6_cpp_args = [
+ apps_cpp_args,
+ '-DQT_NO_KEYWORDS',
+ '-Wno-extra-semi',
+]
-if cxx.has_header_symbol('QOpenGLWidget', 'QOpenGLWidget',
- dependencies : qt5_dep, args : '-fPIC')
- qcam_sources += files([
- 'viewfinder_gl.cpp',
- ])
- qcam_moc_headers += files([
- 'viewfinder_gl.h',
- ])
- qcam_resources += files([
- 'assets/shader/shaders.qrc'
- ])
-endif
-
-# gcc 9 introduced a deprecated-copy warning that is triggered by Qt until
-# Qt 5.13. clang 10 introduced the same warning, but detects more issues
-# that are not fixed in Qt yet. Disable the warning manually in both cases.
-if ((cc.get_id() == 'gcc' and cc.version().version_compare('>=9.0') and
- qt5_dep.version().version_compare('<5.13')) or
- (cc.get_id() == 'clang' and cc.version().version_compare('>=10.0')))
- qt5_cpp_args += ['-Wno-deprecated-copy']
-endif
-
-resources = qt5.preprocess(moc_headers : qcam_moc_headers,
+resources = qt6.preprocess(moc_headers : qcam_moc_headers,
qresources : qcam_resources,
- dependencies : qt5_dep)
+ dependencies : qt6_dep)
qcam = executable('qcam', qcam_sources, resources,
install : true,
@@ -69,6 +54,6 @@ qcam = executable('qcam', qcam_sources, resources,
libatomic,
libcamera_public,
libtiff,
- qt5_dep,
+ qt6_dep,
],
- cpp_args : qt5_cpp_args)
+ cpp_args : qt6_cpp_args)
diff --git a/src/apps/qcam/message_handler.cpp b/src/apps/qcam/message_handler.cpp
index 261623e1..c89714a9 100644
--- a/src/apps/qcam/message_handler.cpp
+++ b/src/apps/qcam/message_handler.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
- * message_handler.cpp - qcam - Log message handling
+ * qcam - Log message handling
*/
#include "message_handler.h"
diff --git a/src/apps/qcam/message_handler.h b/src/apps/qcam/message_handler.h
index 56294d37..92ef74d1 100644
--- a/src/apps/qcam/message_handler.h
+++ b/src/apps/qcam/message_handler.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
- * message_handler.cpp - qcam - Log message handling
+ * Log message handling
*/
#pragma once
diff --git a/src/apps/qcam/viewfinder.h b/src/apps/qcam/viewfinder.h
index a57446e8..914f88ec 100644
--- a/src/apps/qcam/viewfinder.h
+++ b/src/apps/qcam/viewfinder.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * viewfinder.h - qcam - Viewfinder base class
+ * qcam - Viewfinder base class
*/
#pragma once
diff --git a/src/apps/qcam/viewfinder_gl.cpp b/src/apps/qcam/viewfinder_gl.cpp
index f83b99ad..f31956ff 100644
--- a/src/apps/qcam/viewfinder_gl.cpp
+++ b/src/apps/qcam/viewfinder_gl.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Linaro
*
- * viewfinderGL.cpp - OpenGL Viewfinder for rendering by OpenGL shader
+ * OpenGL Viewfinder for rendering by OpenGL shader
*/
#include "viewfinder_gl.h"
@@ -12,6 +12,7 @@
#include <QByteArray>
#include <QFile>
#include <QImage>
+#include <QMatrix4x4>
#include <QStringList>
#include <libcamera/formats.h>
@@ -443,15 +444,11 @@ bool ViewFinderGL::createFragmentShader()
close();
}
- /* Bind shader pipeline for use */
- if (!shaderProgram_.bind()) {
- qWarning() << "[ViewFinderGL]:" << shaderProgram_.log();
- close();
- }
-
attributeVertex = shaderProgram_.attributeLocation("vertexIn");
attributeTexture = shaderProgram_.attributeLocation("textureIn");
+ vertexBuffer_.bind();
+
shaderProgram_.enableAttributeArray(attributeVertex);
shaderProgram_.setAttributeBuffer(attributeVertex,
GL_FLOAT,
@@ -466,6 +463,9 @@ bool ViewFinderGL::createFragmentShader()
2,
2 * sizeof(GLfloat));
+ vertexBuffer_.release();
+
+ projMatrixUniform_ = shaderProgram_.uniformLocation("proj_matrix");
textureUniformY_ = shaderProgram_.uniformLocation("tex_y");
textureUniformU_ = shaderProgram_.uniformLocation("tex_u");
textureUniformV_ = shaderProgram_.uniformLocation("tex_v");
@@ -511,7 +511,7 @@ void ViewFinderGL::initializeGL()
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
- static const GLfloat coordinates[2][4][2]{
+ const GLfloat coordinates[2][4][2]{
{
/* Vertex coordinates */
{ -1.0f, -1.0f },
@@ -535,8 +535,6 @@ void ViewFinderGL::initializeGL()
/* Create Vertex Shader */
if (!createVertexShader())
qWarning() << "[ViewFinderGL]: create vertex shader failed.";
-
- glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}
void ViewFinderGL::doRender()
@@ -805,28 +803,42 @@ void ViewFinderGL::doRender()
shaderProgram_.setUniformValue(textureUniformStrideFactor_,
static_cast<float>(size_.width() - 1) /
(stridePixels - 1));
+
+ /*
+ * Place the viewfinder in the centre of the widget, preserving the
+ * aspect ratio of the image.
+ */
+ QMatrix4x4 projMatrix;
+ QSizeF scaledSize = size_.scaled(size(), Qt::KeepAspectRatio);
+ projMatrix.scale(scaledSize.width() / size().width(),
+ scaledSize.height() / size().height());
+
+ shaderProgram_.setUniformValue(projMatrixUniform_, projMatrix);
}
void ViewFinderGL::paintGL()
{
- if (!fragmentShader_)
+ if (!fragmentShader_) {
if (!createFragmentShader()) {
qWarning() << "[ViewFinderGL]:"
<< "create fragment shader failed.";
}
+ }
- if (image_) {
- glClearColor(0.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- doRender();
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ /* Bind shader pipeline for use. */
+ if (!shaderProgram_.bind()) {
+ qWarning() << "[ViewFinderGL]:" << shaderProgram_.log();
+ close();
}
-}
-void ViewFinderGL::resizeGL(int w, int h)
-{
- glViewport(0, 0, w, h);
+ if (!image_)
+ return;
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ doRender();
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
QSize ViewFinderGL::sizeHint() const
diff --git a/src/apps/qcam/viewfinder_gl.h b/src/apps/qcam/viewfinder_gl.h
index 68c2912d..23c657bc 100644
--- a/src/apps/qcam/viewfinder_gl.h
+++ b/src/apps/qcam/viewfinder_gl.h
@@ -2,8 +2,7 @@
/*
* Copyright (C) 2020, Linaro
*
- * viewfinder_GL.h - OpenGL Viewfinder for rendering by OpenGL shader
- *
+ * OpenGL Viewfinder for rendering by OpenGL shader
*/
#pragma once
@@ -52,7 +51,6 @@ Q_SIGNALS:
protected:
void initializeGL() override;
void paintGL() override;
- void resizeGL(int w, int h) override;
QSize sizeHint() const override;
private:
@@ -89,6 +87,7 @@ private:
/* Common texture parameters */
GLuint textureMinMagFilters_;
+ GLuint projMatrixUniform_;
/* YUV texture parameters */
GLuint textureUniformU_;
diff --git a/src/apps/qcam/viewfinder_qt.cpp b/src/apps/qcam/viewfinder_qt.cpp
index 62ed5e7c..1a238922 100644
--- a/src/apps/qcam/viewfinder_qt.cpp
+++ b/src/apps/qcam/viewfinder_qt.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * viewfinder_qt.cpp - qcam - QPainter-based ViewFinder
+ * qcam - QPainter-based ViewFinder
*/
#include "viewfinder_qt.h"
@@ -18,6 +18,7 @@
#include <QMap>
#include <QMutexLocker>
#include <QPainter>
+#include <QResizeEvent>
#include <QtDebug>
#include "../common/image.h"
@@ -26,23 +27,23 @@
static const QMap<libcamera::PixelFormat, QImage::Format> nativeFormats
{
-#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
{ libcamera::formats::ABGR8888, QImage::Format_RGBX8888 },
{ libcamera::formats::XBGR8888, QImage::Format_RGBX8888 },
-#endif
{ libcamera::formats::ARGB8888, QImage::Format_RGB32 },
{ libcamera::formats::XRGB8888, QImage::Format_RGB32 },
-#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
{ libcamera::formats::RGB888, QImage::Format_BGR888 },
-#endif
{ libcamera::formats::BGR888, QImage::Format_RGB888 },
{ libcamera::formats::RGB565, QImage::Format_RGB16 },
};
ViewFinderQt::ViewFinderQt(QWidget *parent)
- : QWidget(parent), buffer_(nullptr)
+ : QWidget(parent), place_(rect()), buffer_(nullptr)
{
icon_ = QIcon(":camera-off.svg");
+
+ QPalette pal = palette();
+ pal.setColor(QPalette::Window, Qt::black);
+ setPalette(pal);
}
ViewFinderQt::~ViewFinderQt()
@@ -117,6 +118,11 @@ void ViewFinderQt::render(libcamera::FrameBuffer *buffer, Image *image)
}
}
+ /*
+ * Indicate the widget paints all its pixels, to optimize rendering by
+ * skipping erasing the widget before painting.
+ */
+ setAttribute(Qt::WA_OpaquePaintEvent, true);
update();
if (buffer)
@@ -132,6 +138,11 @@ void ViewFinderQt::stop()
buffer_ = nullptr;
}
+ /*
+ * The logo has a transparent background, reenable erasing the widget
+ * before painting.
+ */
+ setAttribute(Qt::WA_OpaquePaintEvent, false);
update();
}
@@ -146,9 +157,23 @@ void ViewFinderQt::paintEvent(QPaintEvent *)
{
QPainter painter(this);
- /* If we have an image, draw it. */
+ painter.setBrush(palette().window());
+
+ /* If we have an image, draw it, with black letterbox rectangles. */
if (!image_.isNull()) {
- painter.drawImage(rect(), image_, image_.rect());
+ if (place_.width() < width()) {
+ QRect rect{ 0, 0, (width() - place_.width()) / 2, height() };
+ painter.drawRect(rect);
+ rect.moveLeft(place_.right());
+ painter.drawRect(rect);
+ } else {
+ QRect rect{ 0, 0, width(), (height() - place_.height()) / 2 };
+ painter.drawRect(rect);
+ rect.moveTop(place_.bottom());
+ painter.drawRect(rect);
+ }
+
+ painter.drawImage(place_, image_, image_.rect());
return;
}
@@ -173,7 +198,6 @@ void ViewFinderQt::paintEvent(QPaintEvent *)
else
point.setY((height() - pixmap_.height()) / 2);
- painter.setBackgroundMode(Qt::OpaqueMode);
painter.drawPixmap(point, pixmap_);
}
@@ -181,3 +205,14 @@ QSize ViewFinderQt::sizeHint() const
{
return size_.isValid() ? size_ : QSize(640, 480);
}
+
+void ViewFinderQt::resizeEvent(QResizeEvent *event)
+{
+ if (!size_.isValid())
+ return;
+
+ place_.setSize(size_.scaled(event->size(), Qt::KeepAspectRatio));
+ place_.moveCenter(rect().center());
+
+ QWidget::resizeEvent(event);
+}
diff --git a/src/apps/qcam/viewfinder_qt.h b/src/apps/qcam/viewfinder_qt.h
index eb3a9988..50fde88e 100644
--- a/src/apps/qcam/viewfinder_qt.h
+++ b/src/apps/qcam/viewfinder_qt.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * viewfinder_qt.h - qcam - QPainter-based ViewFinder
+ * qcam - QPainter-based ViewFinder
*/
#pragma once
@@ -44,6 +44,7 @@ Q_SIGNALS:
protected:
void paintEvent(QPaintEvent *) override;
+ void resizeEvent(QResizeEvent *) override;
QSize sizeHint() const override;
private:
@@ -51,6 +52,7 @@ private:
libcamera::PixelFormat format_;
QSize size_;
+ QRect place_;
/* Camera stopped icon */
QSize vfSize_;