summaryrefslogtreecommitdiff
path: root/src/apps/cam
diff options
context:
space:
mode:
Diffstat (limited to 'src/apps/cam')
-rw-r--r--src/apps/cam/camera_session.cpp5
-rw-r--r--src/apps/cam/camera_session.h2
-rw-r--r--src/apps/cam/dng_writer.cpp653
-rw-r--r--src/apps/cam/dng_writer.h27
-rw-r--r--src/apps/cam/drm.cpp2
-rw-r--r--src/apps/cam/event_loop.cpp150
-rw-r--r--src/apps/cam/event_loop.h68
-rw-r--r--src/apps/cam/file_sink.cpp5
-rw-r--r--src/apps/cam/image.cpp109
-rw-r--r--src/apps/cam/image.h50
-rw-r--r--src/apps/cam/main.cpp7
-rw-r--r--src/apps/cam/meson.build14
-rw-r--r--src/apps/cam/options.cpp1141
-rw-r--r--src/apps/cam/options.h157
-rw-r--r--src/apps/cam/sdl_sink.cpp5
-rw-r--r--src/apps/cam/sdl_texture.h2
-rw-r--r--src/apps/cam/stream_options.cpp134
-rw-r--r--src/apps/cam/stream_options.h28
18 files changed, 18 insertions, 2541 deletions
diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp
index 6b409c98..8fcec630 100644
--- a/src/apps/cam/camera_session.cpp
+++ b/src/apps/cam/camera_session.cpp
@@ -13,9 +13,11 @@
#include <libcamera/control_ids.h>
#include <libcamera/property_ids.h>
+#include "../common/event_loop.h"
+#include "../common/stream_options.h"
+
#include "camera_session.h"
#include "capture_script.h"
-#include "event_loop.h"
#include "file_sink.h"
#ifdef HAVE_KMS
#include "kms_sink.h"
@@ -24,7 +26,6 @@
#ifdef HAVE_SDL
#include "sdl_sink.h"
#endif
-#include "stream_options.h"
using namespace libcamera;
diff --git a/src/apps/cam/camera_session.h b/src/apps/cam/camera_session.h
index d562caae..0bab519f 100644
--- a/src/apps/cam/camera_session.h
+++ b/src/apps/cam/camera_session.h
@@ -21,7 +21,7 @@
#include <libcamera/request.h>
#include <libcamera/stream.h>
-#include "options.h"
+#include "../common/options.h"
class CaptureScript;
class FrameSink;
diff --git a/src/apps/cam/dng_writer.cpp b/src/apps/cam/dng_writer.cpp
deleted file mode 100644
index c945edce..00000000
--- a/src/apps/cam/dng_writer.cpp
+++ /dev/null
@@ -1,653 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2020, Raspberry Pi Ltd
- *
- * dng_writer.cpp - DNG writer
- */
-
-#include "dng_writer.h"
-
-#include <algorithm>
-#include <iostream>
-#include <map>
-
-#include <tiffio.h>
-
-#include <libcamera/control_ids.h>
-#include <libcamera/formats.h>
-#include <libcamera/property_ids.h>
-
-using namespace libcamera;
-
-enum CFAPatternColour : uint8_t {
- CFAPatternRed = 0,
- CFAPatternGreen = 1,
- CFAPatternBlue = 2,
-};
-
-struct FormatInfo {
- uint8_t bitsPerSample;
- CFAPatternColour pattern[4];
- void (*packScanline)(void *output, const void *input,
- unsigned int width);
- void (*thumbScanline)(const FormatInfo &info, void *output,
- const void *input, unsigned int width,
- unsigned int stride);
-};
-
-struct Matrix3d {
- Matrix3d()
- {
- }
-
- Matrix3d(float m0, float m1, float m2,
- float m3, float m4, float m5,
- float m6, float m7, float m8)
- {
- m[0] = m0, m[1] = m1, m[2] = m2;
- m[3] = m3, m[4] = m4, m[5] = m5;
- m[6] = m6, m[7] = m7, m[8] = m8;
- }
-
- Matrix3d(const Span<const float> &span)
- : Matrix3d(span[0], span[1], span[2],
- span[3], span[4], span[5],
- span[6], span[7], span[8])
- {
- }
-
- static Matrix3d diag(float diag0, float diag1, float diag2)
- {
- return Matrix3d(diag0, 0, 0, 0, diag1, 0, 0, 0, diag2);
- }
-
- static Matrix3d identity()
- {
- return Matrix3d(1, 0, 0, 0, 1, 0, 0, 0, 1);
- }
-
- Matrix3d transpose() const
- {
- return { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] };
- }
-
- Matrix3d cofactors() const
- {
- return { m[4] * m[8] - m[5] * m[7],
- -(m[3] * m[8] - m[5] * m[6]),
- m[3] * m[7] - m[4] * m[6],
- -(m[1] * m[8] - m[2] * m[7]),
- m[0] * m[8] - m[2] * m[6],
- -(m[0] * m[7] - m[1] * m[6]),
- m[1] * m[5] - m[2] * m[4],
- -(m[0] * m[5] - m[2] * m[3]),
- m[0] * m[4] - m[1] * m[3] };
- }
-
- Matrix3d adjugate() const
- {
- return cofactors().transpose();
- }
-
- float determinant() const
- {
- return m[0] * (m[4] * m[8] - m[5] * m[7]) -
- m[1] * (m[3] * m[8] - m[5] * m[6]) +
- m[2] * (m[3] * m[7] - m[4] * m[6]);
- }
-
- Matrix3d inverse() const
- {
- return adjugate() * (1.0 / determinant());
- }
-
- Matrix3d operator*(const Matrix3d &other) const
- {
- Matrix3d result;
- for (unsigned int i = 0; i < 3; i++) {
- for (unsigned int j = 0; j < 3; j++) {
- result.m[i * 3 + j] =
- m[i * 3 + 0] * other.m[0 + j] +
- m[i * 3 + 1] * other.m[3 + j] +
- m[i * 3 + 2] * other.m[6 + j];
- }
- }
- return result;
- }
-
- Matrix3d operator*(float f) const
- {
- Matrix3d result;
- for (unsigned int i = 0; i < 9; i++)
- result.m[i] = m[i] * f;
- return result;
- }
-
- float m[9];
-};
-
-void packScanlineSBGGR8(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);
-
- std::copy(in, in + width, out);
-}
-
-void packScanlineSBGGR10P(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);
-
- /* \todo Can this be made more efficient? */
- for (unsigned int x = 0; x < width; x += 4) {
- *out++ = in[0];
- *out++ = (in[4] & 0x03) << 6 | in[1] >> 2;
- *out++ = (in[1] & 0x03) << 6 | (in[4] & 0x0c) << 2 | in[2] >> 4;
- *out++ = (in[2] & 0x0f) << 4 | (in[4] & 0x30) >> 2 | in[3] >> 6;
- *out++ = (in[3] & 0x3f) << 2 | (in[4] & 0xc0) >> 6;
- in += 5;
- }
-}
-
-void packScanlineSBGGR12P(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);
-
- /* \todo Can this be made more efficient? */
- for (unsigned int i = 0; i < width; i += 2) {
- *out++ = in[0];
- *out++ = (in[2] & 0x0f) << 4 | in[1] >> 4;
- *out++ = (in[1] & 0x0f) << 4 | in[2] >> 4;
- in += 3;
- }
-}
-
-void thumbScanlineSBGGRxxP(const FormatInfo &info, void *output,
- const void *input, unsigned int width,
- unsigned int stride)
-{
- const uint8_t *in = static_cast<const uint8_t *>(input);
- uint8_t *out = static_cast<uint8_t *>(output);
-
- /* Number of bytes corresponding to 16 pixels. */
- unsigned int skip = info.bitsPerSample * 16 / 8;
-
- for (unsigned int x = 0; x < width; x++) {
- uint8_t value = (in[0] + in[1] + in[stride] + in[stride + 1]) >> 2;
- *out++ = value;
- *out++ = value;
- *out++ = value;
- in += skip;
- }
-}
-
-void packScanlineIPU3(void *output, const void *input, unsigned int width)
-{
- const uint8_t *in = static_cast<const uint8_t *>(input);
- uint16_t *out = static_cast<uint16_t *>(output);
-
- /*
- * Upscale the 10-bit format to 16-bit as it's not trivial to pack it
- * as 10-bit without gaps.
- *
- * \todo Improve packing to keep the 10-bit sample size.
- */
- unsigned int x = 0;
- while (true) {
- for (unsigned int i = 0; i < 6; i++) {
- *out++ = (in[1] & 0x03) << 14 | (in[0] & 0xff) << 6;
- if (++x >= width)
- return;
-
- *out++ = (in[2] & 0x0f) << 12 | (in[1] & 0xfc) << 4;
- if (++x >= width)
- return;
-
- *out++ = (in[3] & 0x3f) << 10 | (in[2] & 0xf0) << 2;
- if (++x >= width)
- return;
-
- *out++ = (in[4] & 0xff) << 8 | (in[3] & 0xc0) << 0;
- if (++x >= width)
- return;
-
- in += 5;
- }
-
- *out++ = (in[1] & 0x03) << 14 | (in[0] & 0xff) << 6;
- if (++x >= width)
- return;
-
- in += 2;
- }
-}
-
-void thumbScanlineIPU3([[maybe_unused]] const FormatInfo &info, void *output,
- const void *input, unsigned int width,
- unsigned int stride)
-{
- uint8_t *out = static_cast<uint8_t *>(output);
-
- for (unsigned int x = 0; x < width; x++) {
- unsigned int pixel = x * 16;
- unsigned int block = pixel / 25;
- unsigned int pixelInBlock = pixel - block * 25;
-
- /*
- * If the pixel is the last in the block cheat a little and
- * move one pixel backward to avoid reading between two blocks
- * and having to deal with the padding bits.
- */
- if (pixelInBlock == 24)
- pixelInBlock--;
-
- const uint8_t *in = static_cast<const uint8_t *>(input)
- + block * 32 + (pixelInBlock / 4) * 5;
-
- uint16_t val1, val2, val3, val4;
- switch (pixelInBlock % 4) {
- case 0:
- val1 = (in[1] & 0x03) << 14 | (in[0] & 0xff) << 6;
- val2 = (in[2] & 0x0f) << 12 | (in[1] & 0xfc) << 4;
- val3 = (in[stride + 1] & 0x03) << 14 | (in[stride + 0] & 0xff) << 6;
- val4 = (in[stride + 2] & 0x0f) << 12 | (in[stride + 1] & 0xfc) << 4;
- break;
- case 1:
- val1 = (in[2] & 0x0f) << 12 | (in[1] & 0xfc) << 4;
- val2 = (in[3] & 0x3f) << 10 | (in[2] & 0xf0) << 2;
- val3 = (in[stride + 2] & 0x0f) << 12 | (in[stride + 1] & 0xfc) << 4;
- val4 = (in[stride + 3] & 0x3f) << 10 | (in[stride + 2] & 0xf0) << 2;
- break;
- case 2:
- val1 = (in[3] & 0x3f) << 10 | (in[2] & 0xf0) << 2;
- val2 = (in[4] & 0xff) << 8 | (in[3] & 0xc0) << 0;
- val3 = (in[stride + 3] & 0x3f) << 10 | (in[stride + 2] & 0xf0) << 2;
- val4 = (in[stride + 4] & 0xff) << 8 | (in[stride + 3] & 0xc0) << 0;
- break;
- case 3:
- val1 = (in[4] & 0xff) << 8 | (in[3] & 0xc0) << 0;
- val2 = (in[6] & 0x03) << 14 | (in[5] & 0xff) << 6;
- val3 = (in[stride + 4] & 0xff) << 8 | (in[stride + 3] & 0xc0) << 0;
- val4 = (in[stride + 6] & 0x03) << 14 | (in[stride + 5] & 0xff) << 6;
- break;
- }
-
- uint8_t value = (val1 + val2 + val3 + val4) >> 10;
- *out++ = value;
- *out++ = value;
- *out++ = value;
- }
-}
-
-static const std::map<PixelFormat, FormatInfo> formatInfo = {
- { formats::SBGGR8, {
- .bitsPerSample = 8,
- .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SGBRG8, {
- .bitsPerSample = 8,
- .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SGRBG8, {
- .bitsPerSample = 8,
- .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SRGGB8, {
- .bitsPerSample = 8,
- .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SBGGR10_CSI2P, {
- .bitsPerSample = 10,
- .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SGBRG10_CSI2P, {
- .bitsPerSample = 10,
- .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SGRBG10_CSI2P, {
- .bitsPerSample = 10,
- .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SRGGB10_CSI2P, {
- .bitsPerSample = 10,
- .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SBGGR12_CSI2P, {
- .bitsPerSample = 12,
- .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SGBRG12_CSI2P, {
- .bitsPerSample = 12,
- .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SGRBG12_CSI2P, {
- .bitsPerSample = 12,
- .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SRGGB12_CSI2P, {
- .bitsPerSample = 12,
- .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
- } },
- { formats::SBGGR10_IPU3, {
- .bitsPerSample = 16,
- .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineIPU3,
- .thumbScanline = thumbScanlineIPU3,
- } },
- { formats::SGBRG10_IPU3, {
- .bitsPerSample = 16,
- .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineIPU3,
- .thumbScanline = thumbScanlineIPU3,
- } },
- { formats::SGRBG10_IPU3, {
- .bitsPerSample = 16,
- .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineIPU3,
- .thumbScanline = thumbScanlineIPU3,
- } },
- { formats::SRGGB10_IPU3, {
- .bitsPerSample = 16,
- .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineIPU3,
- .thumbScanline = thumbScanlineIPU3,
- } },
-};
-
-int DNGWriter::write(const char *filename, const Camera *camera,
- const StreamConfiguration &config,
- const ControlList &metadata,
- [[maybe_unused]] const FrameBuffer *buffer,
- const void *data)
-{
- const ControlList &cameraProperties = camera->properties();
-
- const auto it = formatInfo.find(config.pixelFormat);
- if (it == formatInfo.cend()) {
- std::cerr << "Unsupported pixel format" << std::endl;
- return -EINVAL;
- }
- const FormatInfo *info = &it->second;
-
- TIFF *tif = TIFFOpen(filename, "w");
- if (!tif) {
- std::cerr << "Failed to open tiff file" << std::endl;
- return -EINVAL;
- }
-
- /*
- * Scanline buffer, has to be large enough to store both a RAW scanline
- * 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];
-
- toff_t rawIFDOffset = 0;
- toff_t exifIFDOffset = 0;
-
- /*
- * Start with a thumbnail in IFD 0 for compatibility with TIFF baseline
- * readers, as required by the TIFF/EP specification. Tags that apply to
- * the whole file are stored here.
- */
- const uint8_t version[] = { 1, 2, 0, 0 };
-
- TIFFSetField(tif, TIFFTAG_DNGVERSION, version);
- TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, version);
- TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
- TIFFSetField(tif, TIFFTAG_MAKE, "libcamera");
-
- const auto &model = cameraProperties.get(properties::Model);
- if (model) {
- TIFFSetField(tif, TIFFTAG_MODEL, model->c_str());
- /* \todo set TIFFTAG_UNIQUECAMERAMODEL. */
- }
-
- TIFFSetField(tif, TIFFTAG_SOFTWARE, "qcam");
- TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
-
- /*
- * Thumbnail-specific tags. The thumbnail is stored as an RGB image
- * with 1/16 of the raw image resolution. Greyscale would save space,
- * but doesn't seem well supported by RawTherapee.
- */
- TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE);
- TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, config.size.width / 16);
- TIFFSetField(tif, TIFFTAG_IMAGELENGTH, config.size.height / 16);
- TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
- TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
- TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
- TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
- TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
- TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
-
- /*
- * Fill in some reasonable colour information in the DNG. We supply
- * the "neutral" colour values which determine the white balance, and the
- * "ColorMatrix1" which converts XYZ to (un-white-balanced) camera RGB.
- * Note that this is not a "proper" colour calibration for the DNG,
- * nonetheless, many tools should be able to render the colours better.
- */
- float neutral[3] = { 1, 1, 1 };
- Matrix3d wbGain = Matrix3d::identity();
- /* From http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html */
- const Matrix3d rgb2xyz(0.4124564, 0.3575761, 0.1804375,
- 0.2126729, 0.7151522, 0.0721750,
- 0.0193339, 0.1191920, 0.9503041);
- Matrix3d ccm = Matrix3d::identity();
- /*
- * Pick a reasonable number eps to protect against singularities. It
- * should be comfortably larger than the point at which we run into
- * numerical trouble, yet smaller than any plausible gain that we might
- * apply to a colour, either explicitly or as part of the colour matrix.
- */
- const double eps = 1e-2;
-
- const auto &colourGains = metadata.get(controls::ColourGains);
- if (colourGains) {
- if ((*colourGains)[0] > eps && (*colourGains)[1] > eps) {
- wbGain = Matrix3d::diag((*colourGains)[0], 1, (*colourGains)[1]);
- neutral[0] = 1.0 / (*colourGains)[0]; /* red */
- neutral[2] = 1.0 / (*colourGains)[1]; /* blue */
- }
- }
-
- const auto &ccmControl = metadata.get(controls::ColourCorrectionMatrix);
- if (ccmControl) {
- Matrix3d ccmSupplied(*ccmControl);
- if (ccmSupplied.determinant() > eps)
- ccm = ccmSupplied;
- }
-
- /*
- * rgb2xyz is known to be invertible, and we've ensured above that both
- * the ccm and wbGain matrices are non-singular, so the product of all
- * three is guaranteed to be invertible too.
- */
- Matrix3d colorMatrix1 = (rgb2xyz * ccm * wbGain).inverse();
-
- TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colorMatrix1.m);
- TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
-
- /*
- * Reserve space for the SubIFD and ExifIFD tags, pointing to the IFD
- * for the raw image and EXIF data respectively. The real offsets will
- * be set later.
- */
- TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset);
- TIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset);
-
- /* 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,
- config.size.width / 16, config.stride);
-
- if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {
- std::cerr << "Failed to write thumbnail scanline"
- << std::endl;
- TIFFClose(tif);
- return -EINVAL;
- }
-
- row += config.stride * 16;
- }
-
- TIFFWriteDirectory(tif);
-
- /* Create a new IFD for the RAW image. */
- const uint16_t cfaRepeatPatternDim[] = { 2, 2 };
- const uint8_t cfaPlaneColor[] = {
- CFAPatternRed,
- CFAPatternGreen,
- CFAPatternBlue
- };
-
- TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
- TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, config.size.width);
- TIFFSetField(tif, TIFFTAG_IMAGELENGTH, config.size.height);
- TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, info->bitsPerSample);
- TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
- TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
- TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
- TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
- TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
- TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfaRepeatPatternDim);
- if (TIFFLIB_VERSION < 20201219)
- TIFFSetField(tif, TIFFTAG_CFAPATTERN, info->pattern);
- else
- TIFFSetField(tif, TIFFTAG_CFAPATTERN, 4, info->pattern);
- TIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, cfaPlaneColor);
- TIFFSetField(tif, TIFFTAG_CFALAYOUT, 1);
-
- const uint16_t blackLevelRepeatDim[] = { 2, 2 };
- float blackLevel[] = { 0.0f, 0.0f, 0.0f, 0.0f };
- uint32_t whiteLevel = (1 << info->bitsPerSample) - 1;
-
- const auto &blackLevels = metadata.get(controls::SensorBlackLevels);
- if (blackLevels) {
- Span<const int32_t, 4> levels = *blackLevels;
-
- /*
- * The black levels control is specified in R, Gr, Gb, B order.
- * Map it to the TIFF tag that is specified in CFA pattern
- * order.
- */
- unsigned int green = (info->pattern[0] == CFAPatternRed ||
- info->pattern[1] == CFAPatternRed)
- ? 0 : 1;
-
- for (unsigned int i = 0; i < 4; ++i) {
- unsigned int level;
-
- switch (info->pattern[i]) {
- case CFAPatternRed:
- level = levels[0];
- break;
- case CFAPatternGreen:
- level = levels[green + 1];
- green = (green + 1) % 2;
- break;
- case CFAPatternBlue:
- default:
- level = levels[3];
- break;
- }
-
- /* Map the 16-bit value to the bits per sample range. */
- blackLevel[i] = level >> (16 - info->bitsPerSample);
- }
- }
-
- TIFFSetField(tif, TIFFTAG_BLACKLEVELREPEATDIM, &blackLevelRepeatDim);
- TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 4, &blackLevel);
- TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &whiteLevel);
-
- /* 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);
-
- if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {
- std::cerr << "Failed to write RAW scanline"
- << std::endl;
- TIFFClose(tif);
- return -EINVAL;
- }
-
- row += config.stride;
- }
-
- /* Checkpoint the IFD to retrieve its offset, and write it out. */
- TIFFCheckpointDirectory(tif);
- rawIFDOffset = TIFFCurrentDirOffset(tif);
- TIFFWriteDirectory(tif);
-
- /* Create a new IFD for the EXIF data and fill it. */
- TIFFCreateEXIFDirectory(tif);
-
- /* Store creation time. */
- time_t rawtime;
- struct tm *timeinfo;
- char strTime[20];
-
- time(&rawtime);
- timeinfo = localtime(&rawtime);
- strftime(strTime, 20, "%Y:%m:%d %H:%M:%S", timeinfo);
-
- /*
- * \todo Handle timezone information by setting OffsetTimeOriginal and
- * OffsetTimeDigitized once libtiff catches up to the specification and
- * has EXIFTAG_ defines to handle them.
- */
- TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, strTime);
- TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, strTime);
-
- const auto &analogGain = metadata.get(controls::AnalogueGain);
- if (analogGain) {
- uint16_t iso = std::min(std::max(*analogGain * 100, 0.0f), 65535.0f);
- TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, &iso);
- }
-
- const auto &exposureTime = metadata.get(controls::ExposureTime);
- if (exposureTime)
- TIFFSetField(tif, EXIFTAG_EXPOSURETIME, *exposureTime / 1e6);
-
- TIFFWriteCustomDirectory(tif, &exifIFDOffset);
-
- /* Update the IFD offsets and close the file. */
- TIFFSetDirectory(tif, 0);
- TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset);
- TIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset);
- TIFFWriteDirectory(tif);
-
- TIFFClose(tif);
-
- return 0;
-}
diff --git a/src/apps/cam/dng_writer.h b/src/apps/cam/dng_writer.h
deleted file mode 100644
index 38f38f62..00000000
--- a/src/apps/cam/dng_writer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2020, Raspberry Pi Ltd
- *
- * dng_writer.h - DNG writer
- */
-
-#pragma once
-
-#ifdef HAVE_TIFF
-#define HAVE_DNG
-
-#include <libcamera/camera.h>
-#include <libcamera/controls.h>
-#include <libcamera/framebuffer.h>
-#include <libcamera/stream.h>
-
-class DNGWriter
-{
-public:
- static int write(const char *filename, const libcamera::Camera *camera,
- const libcamera::StreamConfiguration &config,
- const libcamera::ControlList &metadata,
- const libcamera::FrameBuffer *buffer, const void *data);
-};
-
-#endif /* HAVE_TIFF */
diff --git a/src/apps/cam/drm.cpp b/src/apps/cam/drm.cpp
index 2e4d7985..8779a713 100644
--- a/src/apps/cam/drm.cpp
+++ b/src/apps/cam/drm.cpp
@@ -24,7 +24,7 @@
#include <libdrm/drm_mode.h>
-#include "event_loop.h"
+#include "../common/event_loop.h"
namespace DRM {
diff --git a/src/apps/cam/event_loop.cpp b/src/apps/cam/event_loop.cpp
deleted file mode 100644
index cb83845c..00000000
--- a/src/apps/cam/event_loop.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * event_loop.cpp - cam - Event loop
- */
-
-#include "event_loop.h"
-
-#include <assert.h>
-#include <event2/event.h>
-#include <event2/thread.h>
-#include <iostream>
-
-EventLoop *EventLoop::instance_ = nullptr;
-
-EventLoop::EventLoop()
-{
- assert(!instance_);
-
- evthread_use_pthreads();
- base_ = event_base_new();
- instance_ = this;
-}
-
-EventLoop::~EventLoop()
-{
- instance_ = nullptr;
-
- events_.clear();
- event_base_free(base_);
- libevent_global_shutdown();
-}
-
-EventLoop *EventLoop::instance()
-{
- return instance_;
-}
-
-int EventLoop::exec()
-{
- exitCode_ = -1;
- event_base_loop(base_, EVLOOP_NO_EXIT_ON_EMPTY);
- return exitCode_;
-}
-
-void EventLoop::exit(int code)
-{
- exitCode_ = code;
- event_base_loopbreak(base_);
-}
-
-void EventLoop::callLater(const std::function<void()> &func)
-{
- {
- std::unique_lock<std::mutex> locker(lock_);
- calls_.push_back(func);
- }
-
- event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr);
-}
-
-void EventLoop::addFdEvent(int fd, EventType type,
- const std::function<void()> &callback)
-{
- std::unique_ptr<Event> event = std::make_unique<Event>(callback);
- short events = (type & Read ? EV_READ : 0)
- | (type & Write ? EV_WRITE : 0)
- | EV_PERSIST;
-
- event->event_ = event_new(base_, fd, events, &EventLoop::Event::dispatch,
- event.get());
- if (!event->event_) {
- std::cerr << "Failed to create event for fd " << fd << std::endl;
- return;
- }
-
- int ret = event_add(event->event_, nullptr);
- if (ret < 0) {
- std::cerr << "Failed to add event for fd " << fd << std::endl;
- return;
- }
-
- events_.push_back(std::move(event));
-}
-
-void EventLoop::addTimerEvent(const std::chrono::microseconds period,
- const std::function<void()> &callback)
-{
- std::unique_ptr<Event> event = std::make_unique<Event>(callback);
- event->event_ = event_new(base_, -1, EV_PERSIST, &EventLoop::Event::dispatch,
- event.get());
- if (!event->event_) {
- std::cerr << "Failed to create timer event" << std::endl;
- return;
- }
-
- struct timeval tv;
- tv.tv_sec = period.count() / 1000000ULL;
- tv.tv_usec = period.count() % 1000000ULL;
-
- int ret = event_add(event->event_, &tv);
- if (ret < 0) {
- std::cerr << "Failed to add timer event" << std::endl;
- return;
- }
-
- 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()
-{
- event_del(event_);
- event_free(event_);
-}
-
-void EventLoop::Event::dispatch([[maybe_unused]] int fd,
- [[maybe_unused]] short events, void *arg)
-{
- Event *event = static_cast<Event *>(arg);
- event->callback_();
-}
diff --git a/src/apps/cam/event_loop.h b/src/apps/cam/event_loop.h
deleted file mode 100644
index ef79e8e5..00000000
--- a/src/apps/cam/event_loop.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * event_loop.h - cam - Event loop
- */
-
-#pragma once
-
-#include <chrono>
-#include <functional>
-#include <list>
-#include <memory>
-#include <mutex>
-
-#include <event2/util.h>
-
-struct event_base;
-
-class EventLoop
-{
-public:
- enum EventType {
- Read = 1,
- Write = 2,
- };
-
- EventLoop();
- ~EventLoop();
-
- static EventLoop *instance();
-
- int exec();
- void exit(int code = 0);
-
- void callLater(const std::function<void()> &func);
-
- void addFdEvent(int fd, EventType type,
- const std::function<void()> &handler);
-
- using duration = std::chrono::steady_clock::duration;
- void addTimerEvent(const std::chrono::microseconds period,
- const std::function<void()> &handler);
-
-private:
- struct Event {
- Event(const std::function<void()> &callback);
- ~Event();
-
- static void dispatch(int fd, short events, void *arg);
-
- std::function<void()> callback_;
- struct event *event_;
- };
-
- static EventLoop *instance_;
-
- struct event_base *base_;
- int exitCode_;
-
- std::list<std::function<void()>> calls_;
- 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/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp
index 4dfacc10..b32aad24 100644
--- a/src/apps/cam/file_sink.cpp
+++ b/src/apps/cam/file_sink.cpp
@@ -15,9 +15,10 @@
#include <libcamera/camera.h>
-#include "dng_writer.h"
+#include "../common/dng_writer.h"
+#include "../common/image.h"
+
#include "file_sink.h"
-#include "image.h"
using namespace libcamera;
diff --git a/src/apps/cam/image.cpp b/src/apps/cam/image.cpp
deleted file mode 100644
index fe2cc6da..00000000
--- a/src/apps/cam/image.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2021, Ideas on Board Oy
- *
- * image.cpp - Multi-planar image with access to pixel data
- */
-
-#include "image.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <iostream>
-#include <map>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-using namespace libcamera;
-
-std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode mode)
-{
- std::unique_ptr<Image> image{ new Image() };
-
- assert(!buffer->planes().empty());
-
- int mmapFlags = 0;
-
- if (mode & MapMode::ReadOnly)
- mmapFlags |= PROT_READ;
-
- if (mode & MapMode::WriteOnly)
- mmapFlags |= PROT_WRITE;
-
- struct MappedBufferInfo {
- uint8_t *address = nullptr;
- size_t mapLength = 0;
- size_t dmabufLength = 0;
- };
- std::map<int, MappedBufferInfo> mappedBuffers;
-
- for (const FrameBuffer::Plane &plane : buffer->planes()) {
- const int fd = plane.fd.get();
- if (mappedBuffers.find(fd) == mappedBuffers.end()) {
- const size_t length = lseek(fd, 0, SEEK_END);
- mappedBuffers[fd] = MappedBufferInfo{ nullptr, 0, length };
- }
-
- const size_t length = mappedBuffers[fd].dmabufLength;
-
- if (plane.offset > length ||
- plane.offset + plane.length > length) {
- std::cerr << "plane is out of buffer: buffer length="
- << length << ", plane offset=" << plane.offset
- << ", plane length=" << plane.length
- << std::endl;
- return nullptr;
- }
- size_t &mapLength = mappedBuffers[fd].mapLength;
- mapLength = std::max(mapLength,
- static_cast<size_t>(plane.offset + plane.length));
- }
-
- for (const FrameBuffer::Plane &plane : buffer->planes()) {
- const int fd = plane.fd.get();
- auto &info = mappedBuffers[fd];
- if (!info.address) {
- void *address = mmap(nullptr, info.mapLength, mmapFlags,
- MAP_SHARED, fd, 0);
- if (address == MAP_FAILED) {
- int error = -errno;
- std::cerr << "Failed to mmap plane: "
- << strerror(-error) << std::endl;
- return nullptr;
- }
-
- info.address = static_cast<uint8_t *>(address);
- image->maps_.emplace_back(info.address, info.mapLength);
- }
-
- image->planes_.emplace_back(info.address + plane.offset, plane.length);
- }
-
- return image;
-}
-
-Image::Image() = default;
-
-Image::~Image()
-{
- for (Span<uint8_t> &map : maps_)
- munmap(map.data(), map.size());
-}
-
-unsigned int Image::numPlanes() const
-{
- return planes_.size();
-}
-
-Span<uint8_t> Image::data(unsigned int plane)
-{
- assert(plane <= planes_.size());
- return planes_[plane];
-}
-
-Span<const uint8_t> Image::data(unsigned int plane) const
-{
- assert(plane <= planes_.size());
- return planes_[plane];
-}
diff --git a/src/apps/cam/image.h b/src/apps/cam/image.h
deleted file mode 100644
index 7953b177..00000000
--- a/src/apps/cam/image.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2021, Ideas on Board Oy
- *
- * image.h - Multi-planar image with access to pixel data
- */
-
-#pragma once
-
-#include <memory>
-#include <stdint.h>
-#include <vector>
-
-#include <libcamera/base/class.h>
-#include <libcamera/base/flags.h>
-#include <libcamera/base/span.h>
-
-#include <libcamera/framebuffer.h>
-
-class Image
-{
-public:
- enum class MapMode {
- ReadOnly = 1 << 0,
- WriteOnly = 1 << 1,
- ReadWrite = ReadOnly | WriteOnly,
- };
-
- static std::unique_ptr<Image> fromFrameBuffer(const libcamera::FrameBuffer *buffer,
- MapMode mode);
-
- ~Image();
-
- unsigned int numPlanes() const;
-
- libcamera::Span<uint8_t> data(unsigned int plane);
- libcamera::Span<const uint8_t> data(unsigned int plane) const;
-
-private:
- LIBCAMERA_DISABLE_COPY(Image)
-
- Image();
-
- std::vector<libcamera::Span<uint8_t>> maps_;
- std::vector<libcamera::Span<uint8_t>> planes_;
-};
-
-namespace libcamera {
-LIBCAMERA_FLAGS_ENABLE_OPERATORS(Image::MapMode)
-}
diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp
index d70130e2..5d8a57bc 100644
--- a/src/apps/cam/main.cpp
+++ b/src/apps/cam/main.cpp
@@ -14,11 +14,12 @@
#include <libcamera/libcamera.h>
#include <libcamera/property_ids.h>
+#include "../common/event_loop.h"
+#include "../common/options.h"
+#include "../common/stream_options.h"
+
#include "camera_session.h"
-#include "event_loop.h"
#include "main.h"
-#include "options.h"
-#include "stream_options.h"
using namespace libcamera;
diff --git a/src/apps/cam/meson.build b/src/apps/cam/meson.build
index 297de64f..48c834ac 100644
--- a/src/apps/cam/meson.build
+++ b/src/apps/cam/meson.build
@@ -10,16 +10,12 @@ cam_enabled = true
cam_sources = files([
'camera_session.cpp',
'capture_script.cpp',
- 'event_loop.cpp',
'file_sink.cpp',
'frame_sink.cpp',
- 'image.cpp',
'main.cpp',
- 'options.cpp',
- 'stream_options.cpp',
])
-cam_cpp_args = []
+cam_cpp_args = [apps_cpp_args]
libdrm = dependency('libdrm', required : false)
libjpeg = dependency('libjpeg', required : false)
@@ -49,14 +45,8 @@ if libsdl2.found()
endif
endif
-if libtiff.found()
- cam_cpp_args += ['-DHAVE_TIFF']
- cam_sources += files([
- 'dng_writer.cpp',
- ])
-endif
-
cam = executable('cam', cam_sources,
+ link_with : apps_lib,
dependencies : [
libatomic,
libcamera_public,
diff --git a/src/apps/cam/options.cpp b/src/apps/cam/options.cpp
deleted file mode 100644
index 4f7e8691..00000000
--- a/src/apps/cam/options.cpp
+++ /dev/null
@@ -1,1141 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * options.cpp - cam - Options parsing
- */
-
-#include <assert.h>
-#include <getopt.h>
-#include <iomanip>
-#include <iostream>
-#include <string.h>
-
-#include "options.h"
-
-/**
- * \enum OptionArgument
- * \brief Indicate if an option takes an argument
- *
- * \var OptionArgument::ArgumentNone
- * \brief The option doesn't accept any argument
- *
- * \var OptionArgument::ArgumentRequired
- * \brief The option requires an argument
- *
- * \var OptionArgument::ArgumentOptional
- * \brief The option accepts an optional argument
- */
-
-/**
- * \enum OptionType
- * \brief The type of argument for an option
- *
- * \var OptionType::OptionNone
- * \brief No argument type, used for options that take no argument
- *
- * \var OptionType::OptionInteger
- * \brief Integer argument type, with an optional base prefix (`0` for base 8,
- * `0x` for base 16, none for base 10)
- *
- * \var OptionType::OptionString
- * \brief String argument
- *
- * \var OptionType::OptionKeyValue
- * \brief key=value list argument
- */
-
-/* -----------------------------------------------------------------------------
- * Option
- */
-
-/**
- * \struct Option
- * \brief Store metadata about an option
- *
- * \var Option::opt
- * \brief The option identifier
- *
- * \var Option::type
- * \brief The type of the option argument
- *
- * \var Option::name
- * \brief The option name
- *
- * \var Option::argument
- * \brief Whether the option accepts an optional argument, a mandatory
- * argument, or no argument at all
- *
- * \var Option::argumentName
- * \brief The argument name used in the help text
- *
- * \var Option::help
- * \brief The help text (may be a multi-line string)
- *
- * \var Option::keyValueParser
- * \brief For options of type OptionType::OptionKeyValue, the key-value parser
- * to parse the argument
- *
- * \var Option::isArray
- * \brief Whether the option can appear once or multiple times
- *
- * \var Option::parent
- * \brief The parent option
- *
- * \var Option::children
- * \brief List of child options, storing all options whose parent is this option
- *
- * \fn Option::hasShortOption()
- * \brief Tell if the option has a short option specifier (e.g. `-f`)
- * \return True if the option has a short option specifier, false otherwise
- *
- * \fn Option::hasLongOption()
- * \brief Tell if the option has a long option specifier (e.g. `--foo`)
- * \return True if the option has a long option specifier, false otherwise
- */
-struct Option {
- int opt;
- OptionType type;
- const char *name;
- OptionArgument argument;
- const char *argumentName;
- const char *help;
- KeyValueParser *keyValueParser;
- bool isArray;
- Option *parent;
- std::list<Option> children;
-
- bool hasShortOption() const { return isalnum(opt); }
- bool hasLongOption() const { return name != nullptr; }
- const char *typeName() const;
- std::string optionName() const;
-};
-
-/**
- * \brief Retrieve a string describing the option type
- * \return A string describing the option type
- */
-const char *Option::typeName() const
-{
- switch (type) {
- case OptionNone:
- return "none";
-
- case OptionInteger:
- return "integer";
-
- case OptionString:
- return "string";
-
- case OptionKeyValue:
- return "key=value";
- }
-
- return "unknown";
-}
-
-/**
- * \brief Retrieve a string describing the option name, with leading dashes
- * \return A string describing the option name, as a long option identifier
- * (double dash) if the option has a name, or a short option identifier (single
- * dash) otherwise
- */
-std::string Option::optionName() const
-{
- if (name)
- return "--" + std::string(name);
- else
- return "-" + std::string(1, opt);
-}
-
-/* -----------------------------------------------------------------------------
- * OptionBase<T>
- */
-
-/**
- * \class template<typename T> OptionBase
- * \brief Container to store the values of parsed options
- * \tparam T The type through which options are identified
- *
- * The OptionsBase class is generated by a parser (either OptionsParser or
- * KeyValueParser) when parsing options. It stores values for all the options
- * found, and exposes accessor functions to retrieve them. The options are
- * accessed through an identifier to type \a T, which is an int referencing an
- * Option::opt for OptionsParser, or a std::string referencing an Option::name
- * for KeyValueParser.
- */
-
-/**
- * \fn OptionsBase::OptionsBase()
- * \brief Construct an OptionsBase instance
- *
- * The constructed instance is initially invalid, and will be populated by the
- * options parser.
- */
-
-/**
- * \brief Tell if the stored options list is empty
- * \return True if the container is empty, false otherwise
- */
-template<typename T>
-bool OptionsBase<T>::empty() const
-{
- return values_.empty();
-}
-
-/**
- * \brief Tell if the options parsing completed successfully
- * \return True if the container is returned after successfully parsing
- * options, false if it is returned after an error was detected during parsing
- */
-template<typename T>
-bool OptionsBase<T>::valid() const
-{
- return valid_;
-}
-
-/**
- * \brief Tell if the option \a opt is specified
- * \param[in] opt The option to search for
- * \return True if the \a opt option is set, false otherwise
- */
-template<typename T>
-bool OptionsBase<T>::isSet(const T &opt) const
-{
- return values_.find(opt) != values_.end();
-}
-
-/**
- * \brief Retrieve the value of option \a opt
- * \param[in] opt The option to retrieve
- * \return The value of option \a opt if found, an empty OptionValue otherwise
- */
-template<typename T>
-const OptionValue &OptionsBase<T>::operator[](const T &opt) const
-{
- static const OptionValue empty;
-
- auto it = values_.find(opt);
- if (it != values_.end())
- return it->second;
- return empty;
-}
-
-/**
- * \brief Mark the container as invalid
- *
- * This function can be used in a key-value parser's override of the
- * KeyValueParser::parse() function to mark the returned options as invalid if
- * a validation error occurs.
- */
-template<typename T>
-void OptionsBase<T>::invalidate()
-{
- valid_ = false;
-}
-
-template<typename T>
-bool OptionsBase<T>::parseValue(const T &opt, const Option &option,
- const char *arg)
-{
- OptionValue value;
-
- switch (option.type) {
- case OptionNone:
- break;
-
- case OptionInteger:
- unsigned int integer;
-
- if (arg) {
- char *endptr;
- integer = strtoul(arg, &endptr, 0);
- if (*endptr != '\0')
- return false;
- } else {
- integer = 0;
- }
-
- value = OptionValue(integer);
- break;
-
- case OptionString:
- value = OptionValue(arg ? arg : "");
- break;
-
- case OptionKeyValue:
- KeyValueParser *kvParser = option.keyValueParser;
- KeyValueParser::Options keyValues = kvParser->parse(arg);
- if (!keyValues.valid())
- return false;
-
- value = OptionValue(keyValues);
- break;
- }
-
- if (option.isArray)
- values_[opt].addValue(value);
- else
- values_[opt] = value;
-
- return true;
-}
-
-template class OptionsBase<int>;
-template class OptionsBase<std::string>;
-
-/* -----------------------------------------------------------------------------
- * KeyValueParser
- */
-
-/**
- * \class KeyValueParser
- * \brief A specialized parser for list of key-value pairs
- *
- * The KeyValueParser is an options parser for comma-separated lists of
- * `key=value` pairs. The supported keys are added to the parser with
- * addOption(). A given key can only appear once in the parsed list.
- *
- * Instances of this class can be passed to the OptionsParser::addOption()
- * function to create options that take key-value pairs as an option argument.
- * Specialized versions of the key-value parser can be created by inheriting
- * from this class, to pre-build the options list in the constructor, and to add
- * custom validation by overriding the parse() function.
- */
-
-/**
- * \class KeyValueParser::Options
- * \brief An option list generated by the key-value parser
- *
- * This is a specialization of OptionsBase with the option reference type set to
- * std::string.
- */
-
-KeyValueParser::KeyValueParser() = default;
-KeyValueParser::~KeyValueParser() = default;
-
-/**
- * \brief Add a supported option to the parser
- * \param[in] name The option name, corresponding to the key name in the
- * key=value pair. The name shall be unique.
- * \param[in] type The type of the value in the key=value pair
- * \param[in] help The help text
- * \param[in] argument Whether the value is optional, mandatory or not allowed.
- * Shall be ArgumentNone if \a type is OptionNone.
- *
- * \sa OptionsParser
- *
- * \return True if the option was added successfully, false if an error
- * occurred.
- */
-bool KeyValueParser::addOption(const char *name, OptionType type,
- const char *help, OptionArgument argument)
-{
- if (!name)
- return false;
- if (!help || help[0] == '\0')
- return false;
- if (argument != ArgumentNone && type == OptionNone)
- return false;
-
- /* Reject duplicate options. */
- if (optionsMap_.find(name) != optionsMap_.end())
- return false;
-
- optionsMap_[name] = Option({ 0, type, name, argument, nullptr,
- help, nullptr, false, nullptr, {} });
- return true;
-}
-
-/**
- * \brief Parse a string containing a list of key-value pairs
- * \param[in] arguments The key-value pairs string to parse
- *
- * If a parsing error occurs, the parsing stops and the function returns an
- * invalid container. The container is populated with the options successfully
- * parsed so far.
- *
- * \return A valid container with the list of parsed options on success, or an
- * invalid container otherwise
- */
-KeyValueParser::Options KeyValueParser::parse(const char *arguments)
-{
- Options options;
-
- for (const char *pair = arguments; *arguments != '\0'; pair = arguments) {
- const char *comma = strchrnul(arguments, ',');
- size_t len = comma - pair;
-
- /* Skip over the comma. */
- arguments = *comma == ',' ? comma + 1 : comma;
-
- /* Skip to the next pair if the pair is empty. */
- if (!len)
- continue;
-
- std::string key;
- std::string value;
-
- const char *separator = static_cast<const char *>(memchr(pair, '=', len));
- if (!separator) {
- key = std::string(pair, len);
- value = "";
- } else {
- key = std::string(pair, separator - pair);
- value = std::string(separator + 1, comma - separator - 1);
- }
-
- /* The key is mandatory, the value might be optional. */
- if (key.empty())
- continue;
-
- if (optionsMap_.find(key) == optionsMap_.end()) {
- std::cerr << "Invalid option " << key << std::endl;
- return options;
- }
-
- OptionArgument arg = optionsMap_[key].argument;
- if (value.empty() && arg == ArgumentRequired) {
- std::cerr << "Option " << key << " requires an argument"
- << std::endl;
- return options;
- } else if (!value.empty() && arg == ArgumentNone) {
- std::cerr << "Option " << key << " takes no argument"
- << std::endl;
- return options;
- }
-
- const Option &option = optionsMap_[key];
- if (!options.parseValue(key, option, value.c_str())) {
- std::cerr << "Failed to parse '" << value << "' as "
- << option.typeName() << " for option " << key
- << std::endl;
- return options;
- }
- }
-
- options.valid_ = true;
- return options;
-}
-
-unsigned int KeyValueParser::maxOptionLength() const
-{
- unsigned int maxLength = 0;
-
- for (auto const &iter : optionsMap_) {
- const Option &option = iter.second;
- unsigned int length = 10 + strlen(option.name);
- if (option.argument != ArgumentNone)
- length += 1 + strlen(option.typeName());
- if (option.argument == ArgumentOptional)
- length += 2;
-
- if (length > maxLength)
- maxLength = length;
- }
-
- return maxLength;
-}
-
-void KeyValueParser::usage(int indent)
-{
- for (auto const &iter : optionsMap_) {
- const Option &option = iter.second;
- std::string argument = std::string(" ") + option.name;
-
- if (option.argument != ArgumentNone) {
- if (option.argument == ArgumentOptional)
- argument += "[=";
- else
- argument += "=";
- argument += option.typeName();
- if (option.argument == ArgumentOptional)
- argument += "]";
- }
-
- std::cerr << std::setw(indent) << argument;
-
- for (const char *help = option.help, *end = help; end;) {
- end = strchr(help, '\n');
- if (end) {
- std::cerr << std::string(help, end - help + 1);
- std::cerr << std::setw(indent) << " ";
- help = end + 1;
- } else {
- std::cerr << help << std::endl;
- }
- }
- }
-}
-
-/* -----------------------------------------------------------------------------
- * OptionValue
- */
-
-/**
- * \class OptionValue
- * \brief Container to store the value of an option
- *
- * The OptionValue class is a variant-type container to store the value of an
- * option. It supports empty values, integers, strings, key-value lists, as well
- * as arrays of those types. For array values, all array elements shall have the
- * same type.
- *
- * OptionValue instances are organized in a tree-based structure that matches
- * the parent-child relationship of the options added to the parser. Children
- * are retrieved with the children() function, and are stored as an
- * OptionsBase<int>.
- */
-
-/**
- * \enum OptionValue::ValueType
- * \brief The option value type
- *
- * \var OptionValue::ValueType::ValueNone
- * \brief Empty value
- *
- * \var OptionValue::ValueType::ValueInteger
- * \brief Integer value (int)
- *
- * \var OptionValue::ValueType::ValueString
- * \brief String value (std::string)
- *
- * \var OptionValue::ValueType::ValueKeyValue
- * \brief Key-value list value (KeyValueParser::Options)
- *
- * \var OptionValue::ValueType::ValueArray
- * \brief Array value
- */
-
-/**
- * \brief Construct an empty OptionValue instance
- *
- * The value type is set to ValueType::ValueNone.
- */
-OptionValue::OptionValue()
- : type_(ValueNone), integer_(0)
-{
-}
-
-/**
- * \brief Construct an integer OptionValue instance
- * \param[in] value The integer value
- *
- * The value type is set to ValueType::ValueInteger.
- */
-OptionValue::OptionValue(int value)
- : type_(ValueInteger), integer_(value)
-{
-}
-
-/**
- * \brief Construct a string OptionValue instance
- * \param[in] value The string value
- *
- * The value type is set to ValueType::ValueString.
- */
-OptionValue::OptionValue(const char *value)
- : type_(ValueString), integer_(0), string_(value)
-{
-}
-
-/**
- * \brief Construct a string OptionValue instance
- * \param[in] value The string value
- *
- * The value type is set to ValueType::ValueString.
- */
-OptionValue::OptionValue(const std::string &value)
- : type_(ValueString), integer_(0), string_(value)
-{
-}
-
-/**
- * \brief Construct a key-value OptionValue instance
- * \param[in] value The key-value list
- *
- * The value type is set to ValueType::ValueKeyValue.
- */
-OptionValue::OptionValue(const KeyValueParser::Options &value)
- : type_(ValueKeyValue), integer_(0), keyValues_(value)
-{
-}
-
-/**
- * \brief Add an entry to an array value
- * \param[in] value The entry value
- *
- * This function can only be called if the OptionValue type is
- * ValueType::ValueNone or ValueType::ValueArray. Upon return, the type will be
- * set to ValueType::ValueArray.
- */
-void OptionValue::addValue(const OptionValue &value)
-{
- assert(type_ == ValueNone || type_ == ValueArray);
-
- type_ = ValueArray;
- array_.push_back(value);
-}
-
-/**
- * \fn OptionValue::type()
- * \brief Retrieve the value type
- * \return The value type
- */
-
-/**
- * \fn OptionValue::empty()
- * \brief Check if the value is empty
- * \return True if the value is empty (type set to ValueType::ValueNone), or
- * false otherwise
- */
-
-/**
- * \brief Cast the value to an int
- * \return The option value as an int, or 0 if the value type isn't
- * ValueType::ValueInteger
- */
-OptionValue::operator int() const
-{
- return toInteger();
-}
-
-/**
- * \brief Cast the value to a std::string
- * \return The option value as an std::string, or an empty string if the value
- * type isn't ValueType::ValueString
- */
-OptionValue::operator std::string() const
-{
- return toString();
-}
-
-/**
- * \brief Retrieve the value as an int
- * \return The option value as an int, or 0 if the value type isn't
- * ValueType::ValueInteger
- */
-int OptionValue::toInteger() const
-{
- if (type_ != ValueInteger)
- return 0;
-
- return integer_;
-}
-
-/**
- * \brief Retrieve the value as a std::string
- * \return The option value as a std::string, or an empty string if the value
- * type isn't ValueType::ValueString
- */
-std::string OptionValue::toString() const
-{
- if (type_ != ValueString)
- return std::string();
-
- return string_;
-}
-
-/**
- * \brief Retrieve the value as a key-value list
- *
- * The behaviour is undefined if the value type isn't ValueType::ValueKeyValue.
- *
- * \return The option value as a KeyValueParser::Options
- */
-const KeyValueParser::Options &OptionValue::toKeyValues() const
-{
- assert(type_ == ValueKeyValue);
- return keyValues_;
-}
-
-/**
- * \brief Retrieve the value as an array
- *
- * The behaviour is undefined if the value type isn't ValueType::ValueArray.
- *
- * \return The option value as a std::vector of OptionValue
- */
-const std::vector<OptionValue> &OptionValue::toArray() const
-{
- assert(type_ == ValueArray);
- return array_;
-}
-
-/**
- * \brief Retrieve the list of child values
- * \return The list of child values
- */
-const OptionsParser::Options &OptionValue::children() const
-{
- return children_;
-}
-
-/* -----------------------------------------------------------------------------
- * OptionsParser
- */
-
-/**
- * \class OptionsParser
- * \brief A command line options parser
- *
- * The OptionsParser class is an easy to use options parser for POSIX-style
- * command line options. Supports short (e.g. `-f`) and long (e.g. `--foo`)
- * options, optional and mandatory arguments, automatic parsing arguments for
- * integer types and comma-separated list of key=value pairs, and multi-value
- * arguments. It handles help text generation automatically.
- *
- * An OptionsParser instance is initialized by adding supported options with
- * addOption(). Options are specified by an identifier and a name. If the
- * identifier is an alphanumeric character, it will be used by the parser as a
- * short option identifier (e.g. `-f`). The name, if specified, will be used as
- * a long option identifier (e.g. `--foo`). It should not include the double
- * dashes. The name is optional if the option identifier is an alphanumeric
- * character and mandatory otherwise.
- *
- * An option has a mandatory help text, which is used to print the full options
- * list with the usage() function. The help text may be a multi-line string.
- * Correct indentation of the help text is handled automatically.
- *
- * Options accept arguments when created with OptionArgument::ArgumentRequired
- * or OptionArgument::ArgumentOptional. If the argument is required, it can be
- * specified as a positional argument after the option (e.g. `-f bar`,
- * `--foo bar`), collated with the short option (e.g. `-fbar`) or separated from
- * the long option by an equal sign (e.g. `--foo=bar`'). When the argument is
- * optional, it must be collated with the short option or separated from the
- * long option by an equal sign.
- *
- * If an option has a required or optional argument, an argument name must be
- * set when adding the option. The argument name is used in the help text as a
- * place holder for an argument value. For instance, a `--write` option that
- * takes a file name as an argument could set the argument name to `filename`,
- * and the help text would display `--write filename`. This is only used to
- * clarify the help text and has no effect on option parsing.
- *
- * The option type tells the parser how to process the argument. Arguments for
- * string options (OptionType::OptionString) are stored as-is without any
- * processing. Arguments for integer options (OptionType::OptionInteger) are
- * converted to an integer value, using an optional base prefix (`0` for base 8,
- * `0x` for base 16, none for base 10). Arguments for key-value options are
- * parsed by a KeyValueParser given to addOption().
- *
- * By default, a given option can appear once only in the parsed command line.
- * If the option is created as an array option, the parser will accept multiple
- * instances of the option. The order in which identical options are specified
- * is preserved in the values of an array option.
- *
- * After preparing the parser, it can be used any number of times to parse
- * command line options with the parse() function. The function returns an
- * Options instance that stores the values for the parsed options. The
- * Options::isSet() function can be used to test if an option has been found,
- * and is the only way to access options that take no argument (specified by
- * OptionType::OptionNone and OptionArgument::ArgumentNone). For options that
- * accept an argument, the option value can be access by Options::operator[]()
- * using the option identifier as the key. The order in which different options
- * are specified on the command line isn't preserved.
- *
- * Options can be created with parent-child relationships to organize them as a
- * tree instead of a flat list. When parsing a command line, the child options
- * are considered related to the parent option that precedes them. This is
- * useful when the parent is an array option. The Options values list generated
- * by the parser then turns into a tree, which each parent value storing the
- * values of child options that follow that instance of the parent option.
- * For instance, with a `capture` option specified as a child of a `camera`
- * array option, parsing the command line
- *
- * `--camera 1 --capture=10 --camera 2 --capture=20`
- *
- * will return an Options instance containing a single OptionValue instance of
- * array type, for the `camera` option. The OptionValue will contain two
- * entries, with the first entry containing the integer value 1 and the second
- * entry the integer value 2. Each of those entries will in turn store an
- * Options instance that contains the respective children. The first entry will
- * store in its children a `capture` option of value 10, and the second entry a
- * `capture` option of value 20.
- *
- * The command line
- *
- * `--capture=10 --camera 1`
- *
- * would result in a parsing error, as the `capture` option has no preceding
- * `camera` option on the command line.
- */
-
-/**
- * \class OptionsParser::Options
- * \brief An option list generated by the options parser
- *
- * This is a specialization of OptionsBase with the option reference type set to
- * int.
- */
-
-OptionsParser::OptionsParser() = default;
-OptionsParser::~OptionsParser() = default;
-
-/**
- * \brief Add an option to the parser
- * \param[in] opt The option identifier
- * \param[in] type The type of the option argument
- * \param[in] help The help text (may be a multi-line string)
- * \param[in] name The option name
- * \param[in] argument Whether the option accepts an optional argument, a
- * mandatory argument, or no argument at all
- * \param[in] argumentName The argument name used in the help text
- * \param[in] array Whether the option can appear once or multiple times
- * \param[in] parent The identifier of the parent option (optional)
- *
- * \return True if the option was added successfully, false if an error
- * occurred.
- */
-bool OptionsParser::addOption(int opt, OptionType type, const char *help,
- const char *name, OptionArgument argument,
- const char *argumentName, bool array, int parent)
-{
- /*
- * Options must have at least a short or long name, and a text message.
- * If an argument is accepted, it must be described by argumentName.
- */
- if (!isalnum(opt) && !name)
- return false;
- if (!help || help[0] == '\0')
- return false;
- if (argument != ArgumentNone && !argumentName)
- return false;
-
- /* Reject duplicate options. */
- if (optionsMap_.find(opt) != optionsMap_.end())
- return false;
-
- /*
- * If a parent is specified, create the option as a child of its parent.
- * Otherwise, create it in the parser's options list.
- */
- Option *option;
-
- if (parent) {
- auto iter = optionsMap_.find(parent);
- if (iter == optionsMap_.end())
- return false;
-
- Option *parentOpt = iter->second;
- parentOpt->children.push_back({
- opt, type, name, argument, argumentName, help, nullptr,
- array, parentOpt, {}
- });
- option = &parentOpt->children.back();
- } else {
- options_.push_back({ opt, type, name, argument, argumentName,
- help, nullptr, array, nullptr, {} });
- option = &options_.back();
- }
-
- optionsMap_[opt] = option;
-
- return true;
-}
-
-/**
- * \brief Add a key-value pair option to the parser
- * \param[in] opt The option identifier
- * \param[in] parser The KeyValueParser for the option value
- * \param[in] help The help text (may be a multi-line string)
- * \param[in] name The option name
- * \param[in] array Whether the option can appear once or multiple times
- *
- * \sa Option
- *
- * \return True if the option was added successfully, false if an error
- * occurred.
- */
-bool OptionsParser::addOption(int opt, KeyValueParser *parser, const char *help,
- const char *name, bool array, int parent)
-{
- if (!addOption(opt, OptionKeyValue, help, name, ArgumentRequired,
- "key=value[,key=value,...]", array, parent))
- return false;
-
- optionsMap_[opt]->keyValueParser = parser;
- return true;
-}
-
-/**
- * \brief Parse command line arguments
- * \param[in] argc The number of arguments in the \a argv array
- * \param[in] argv The array of arguments
- *
- * If a parsing error occurs, the parsing stops, the function prints an error
- * message that identifies the invalid argument, prints usage information with
- * usage(), and returns an invalid container. The container is populated with
- * the options successfully parsed so far.
- *
- * \return A valid container with the list of parsed options on success, or an
- * invalid container otherwise
- */
-OptionsParser::Options OptionsParser::parse(int argc, char **argv)
-{
- OptionsParser::Options options;
-
- /*
- * Allocate short and long options arrays large enough to contain all
- * options.
- */
- char shortOptions[optionsMap_.size() * 3 + 2];
- struct option longOptions[optionsMap_.size() + 1];
- unsigned int ids = 0;
- unsigned int idl = 0;
-
- shortOptions[ids++] = ':';
-
- for (const auto [opt, option] : optionsMap_) {
- if (option->hasShortOption()) {
- shortOptions[ids++] = opt;
- if (option->argument != ArgumentNone)
- shortOptions[ids++] = ':';
- if (option->argument == ArgumentOptional)
- shortOptions[ids++] = ':';
- }
-
- if (option->hasLongOption()) {
- longOptions[idl].name = option->name;
-
- switch (option->argument) {
- case ArgumentNone:
- longOptions[idl].has_arg = no_argument;
- break;
- case ArgumentRequired:
- longOptions[idl].has_arg = required_argument;
- break;
- case ArgumentOptional:
- longOptions[idl].has_arg = optional_argument;
- break;
- }
-
- longOptions[idl].flag = 0;
- longOptions[idl].val = option->opt;
- idl++;
- }
- }
-
- shortOptions[ids] = '\0';
- memset(&longOptions[idl], 0, sizeof(longOptions[idl]));
-
- opterr = 0;
-
- while (true) {
- int c = getopt_long(argc, argv, shortOptions, longOptions, nullptr);
-
- if (c == -1)
- break;
-
- if (c == '?' || c == ':') {
- if (c == '?')
- std::cerr << "Invalid option ";
- else
- std::cerr << "Missing argument for option ";
- std::cerr << argv[optind - 1] << std::endl;
-
- usage();
- return options;
- }
-
- const Option &option = *optionsMap_[c];
- if (!parseValue(option, optarg, &options)) {
- usage();
- return options;
- }
- }
-
- if (optind < argc) {
- std::cerr << "Invalid non-option argument '" << argv[optind]
- << "'" << std::endl;
- usage();
- return options;
- }
-
- options.valid_ = true;
- return options;
-}
-
-/**
- * \brief Print usage text to std::cerr
- *
- * The usage text list all the supported option with their arguments. It is
- * generated automatically from the options added to the parser. Caller of this
- * function may print additional usage information for the application before
- * the list of options.
- */
-void OptionsParser::usage()
-{
- unsigned int indent = 0;
-
- for (const auto &opt : optionsMap_) {
- const Option *option = opt.second;
- unsigned int length = 14;
- if (option->hasLongOption())
- length += 2 + strlen(option->name);
- if (option->argument != ArgumentNone)
- length += 1 + strlen(option->argumentName);
- if (option->argument == ArgumentOptional)
- length += 2;
- if (option->isArray)
- length += 4;
-
- if (length > indent)
- indent = length;
-
- if (option->keyValueParser) {
- length = option->keyValueParser->maxOptionLength();
- if (length > indent)
- indent = length;
- }
- }
-
- indent = (indent + 7) / 8 * 8;
-
- std::cerr << "Options:" << std::endl;
-
- std::ios_base::fmtflags f(std::cerr.flags());
- std::cerr << std::left;
-
- usageOptions(options_, indent);
-
- std::cerr.flags(f);
-}
-
-void OptionsParser::usageOptions(const std::list<Option> &options,
- unsigned int indent)
-{
- std::vector<const Option *> parentOptions;
-
- for (const Option &option : options) {
- std::string argument;
- if (option.hasShortOption())
- argument = std::string(" -")
- + static_cast<char>(option.opt);
- else
- argument = " ";
-
- if (option.hasLongOption()) {
- if (option.hasShortOption())
- argument += ", ";
- else
- argument += " ";
- argument += std::string("--") + option.name;
- }
-
- if (option.argument != ArgumentNone) {
- if (option.argument == ArgumentOptional)
- argument += "[=";
- else
- argument += " ";
- argument += option.argumentName;
- if (option.argument == ArgumentOptional)
- argument += "]";
- }
-
- if (option.isArray)
- argument += " ...";
-
- std::cerr << std::setw(indent) << argument;
-
- for (const char *help = option.help, *end = help; end; ) {
- end = strchr(help, '\n');
- if (end) {
- std::cerr << std::string(help, end - help + 1);
- std::cerr << std::setw(indent) << " ";
- help = end + 1;
- } else {
- std::cerr << help << std::endl;
- }
- }
-
- if (option.keyValueParser)
- option.keyValueParser->usage(indent);
-
- if (!option.children.empty())
- parentOptions.push_back(&option);
- }
-
- if (parentOptions.empty())
- return;
-
- for (const Option *option : parentOptions) {
- std::cerr << std::endl << "Options valid in the context of "
- << option->optionName() << ":" << std::endl;
- usageOptions(option->children, indent);
- }
-}
-
-std::tuple<OptionsParser::Options *, const Option *>
-OptionsParser::childOption(const Option *parent, Options *options)
-{
- /*
- * The parent argument points to the parent of the leaf node Option,
- * and the options argument to the root node of the Options tree. Use
- * recursive calls to traverse the Option tree up to the root node while
- * traversing the Options tree down to the leaf node:
- */
-
- /*
- * - If we have no parent, we've reached the root node of the Option
- * tree, the options argument is what we need.
- */
- if (!parent)
- return { options, nullptr };
-
- /*
- * - If the parent has a parent, use recursion to move one level up the
- * Option tree. This returns the Options corresponding to parent, or
- * nullptr if a suitable Options child isn't found.
- */
- if (parent->parent) {
- const Option *error;
- std::tie(options, error) = childOption(parent->parent, options);
-
- /* Propagate the error all the way back up the call stack. */
- if (!error)
- return { options, error };
- }
-
- /*
- * - The parent has no parent, we're now one level down the root.
- * Return the Options child corresponding to the parent. The child may
- * not exist if options are specified in an incorrect order.
- */
- if (!options->isSet(parent->opt))
- return { nullptr, parent };
-
- /*
- * If the child value is of array type, children are not stored in the
- * value .children() list, but in the .children() of the value's array
- * elements. Use the last array element in that case, as a child option
- * relates to the last instance of its parent option.
- */
- const OptionValue *value = &(*options)[parent->opt];
- if (value->type() == OptionValue::ValueArray)
- value = &value->toArray().back();
-
- return { const_cast<Options *>(&value->children()), nullptr };
-}
-
-bool OptionsParser::parseValue(const Option &option, const char *arg,
- Options *options)
-{
- const Option *error;
-
- std::tie(options, error) = childOption(option.parent, options);
- if (error) {
- std::cerr << "Option " << option.optionName() << " requires a "
- << error->optionName() << " context" << std::endl;
- return false;
- }
-
- if (!options->parseValue(option.opt, option, arg)) {
- std::cerr << "Can't parse " << option.typeName()
- << " argument for option " << option.optionName()
- << std::endl;
- return false;
- }
-
- return true;
-}
diff --git a/src/apps/cam/options.h b/src/apps/cam/options.h
deleted file mode 100644
index 4ddd4987..00000000
--- a/src/apps/cam/options.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * options.h - cam - Options parsing
- */
-
-#pragma once
-
-#include <ctype.h>
-#include <list>
-#include <map>
-#include <tuple>
-#include <vector>
-
-class KeyValueParser;
-class OptionValue;
-struct Option;
-
-enum OptionArgument {
- ArgumentNone,
- ArgumentRequired,
- ArgumentOptional,
-};
-
-enum OptionType {
- OptionNone,
- OptionInteger,
- OptionString,
- OptionKeyValue,
-};
-
-template<typename T>
-class OptionsBase
-{
-public:
- OptionsBase() : valid_(false) {}
-
- bool empty() const;
- bool valid() const;
- bool isSet(const T &opt) const;
- const OptionValue &operator[](const T &opt) const;
-
- void invalidate();
-
-private:
- friend class KeyValueParser;
- friend class OptionsParser;
-
- bool parseValue(const T &opt, const Option &option, const char *value);
-
- std::map<T, OptionValue> values_;
- bool valid_;
-};
-
-class KeyValueParser
-{
-public:
- class Options : public OptionsBase<std::string>
- {
- };
-
- KeyValueParser();
- virtual ~KeyValueParser();
-
- bool addOption(const char *name, OptionType type, const char *help,
- OptionArgument argument = ArgumentNone);
-
- virtual Options parse(const char *arguments);
-
-private:
- KeyValueParser(const KeyValueParser &) = delete;
- KeyValueParser &operator=(const KeyValueParser &) = delete;
-
- friend class OptionsParser;
- unsigned int maxOptionLength() const;
- void usage(int indent);
-
- std::map<std::string, Option> optionsMap_;
-};
-
-class OptionsParser
-{
-public:
- class Options : public OptionsBase<int>
- {
- };
-
- OptionsParser();
- ~OptionsParser();
-
- bool addOption(int opt, OptionType type, const char *help,
- const char *name = nullptr,
- OptionArgument argument = ArgumentNone,
- const char *argumentName = nullptr, bool array = false,
- int parent = 0);
- bool addOption(int opt, KeyValueParser *parser, const char *help,
- const char *name = nullptr, bool array = false,
- int parent = 0);
-
- Options parse(int argc, char *argv[]);
- void usage();
-
-private:
- OptionsParser(const OptionsParser &) = delete;
- OptionsParser &operator=(const OptionsParser &) = delete;
-
- void usageOptions(const std::list<Option> &options, unsigned int indent);
-
- std::tuple<OptionsParser::Options *, const Option *>
- childOption(const Option *parent, Options *options);
- bool parseValue(const Option &option, const char *arg, Options *options);
-
- std::list<Option> options_;
- std::map<unsigned int, Option *> optionsMap_;
-};
-
-class OptionValue
-{
-public:
- enum ValueType {
- ValueNone,
- ValueInteger,
- ValueString,
- ValueKeyValue,
- ValueArray,
- };
-
- OptionValue();
- OptionValue(int value);
- OptionValue(const char *value);
- OptionValue(const std::string &value);
- OptionValue(const KeyValueParser::Options &value);
-
- void addValue(const OptionValue &value);
-
- ValueType type() const { return type_; }
- bool empty() const { return type_ == ValueType::ValueNone; }
-
- operator int() const;
- operator std::string() const;
-
- int toInteger() const;
- std::string toString() const;
- const KeyValueParser::Options &toKeyValues() const;
- const std::vector<OptionValue> &toArray() const;
-
- const OptionsParser::Options &children() const;
-
-private:
- ValueType type_;
- int integer_;
- std::string string_;
- KeyValueParser::Options keyValues_;
- std::vector<OptionValue> array_;
- OptionsParser::Options children_;
-};
diff --git a/src/apps/cam/sdl_sink.cpp b/src/apps/cam/sdl_sink.cpp
index ee177227..a2f4abc1 100644
--- a/src/apps/cam/sdl_sink.cpp
+++ b/src/apps/cam/sdl_sink.cpp
@@ -19,8 +19,9 @@
#include <libcamera/camera.h>
#include <libcamera/formats.h>
-#include "event_loop.h"
-#include "image.h"
+#include "../common/event_loop.h"
+#include "../common/image.h"
+
#ifdef HAVE_LIBJPEG
#include "sdl_texture_mjpg.h"
#endif
diff --git a/src/apps/cam/sdl_texture.h b/src/apps/cam/sdl_texture.h
index 6ccd85ea..3993dd46 100644
--- a/src/apps/cam/sdl_texture.h
+++ b/src/apps/cam/sdl_texture.h
@@ -11,7 +11,7 @@
#include <SDL2/SDL.h>
-#include "image.h"
+#include "../common/image.h"
class SDLTexture
{
diff --git a/src/apps/cam/stream_options.cpp b/src/apps/cam/stream_options.cpp
deleted file mode 100644
index 3a5625f5..00000000
--- a/src/apps/cam/stream_options.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2020, Raspberry Pi Ltd
- *
- * stream_options.cpp - Helper to parse options for streams
- */
-#include "stream_options.h"
-
-#include <iostream>
-
-#include <libcamera/color_space.h>
-
-using namespace libcamera;
-
-StreamKeyValueParser::StreamKeyValueParser()
-{
- addOption("role", OptionString,
- "Role for the stream (viewfinder, video, still, raw)",
- ArgumentRequired);
- addOption("width", OptionInteger, "Width in pixels",
- ArgumentRequired);
- addOption("height", OptionInteger, "Height in pixels",
- ArgumentRequired);
- addOption("pixelformat", OptionString, "Pixel format name",
- ArgumentRequired);
- addOption("colorspace", OptionString, "Color space",
- ArgumentRequired);
-}
-
-KeyValueParser::Options StreamKeyValueParser::parse(const char *arguments)
-{
- KeyValueParser::Options options = KeyValueParser::parse(arguments);
- StreamRole role;
-
- if (options.valid() && options.isSet("role") &&
- !parseRole(&role, options)) {
- std::cerr << "Unknown stream role "
- << options["role"].toString() << std::endl;
- options.invalidate();
- }
-
- return options;
-}
-
-StreamRoles StreamKeyValueParser::roles(const OptionValue &values)
-{
- /* If no configuration values to examine default to viewfinder. */
- if (values.empty())
- return { StreamRole::Viewfinder };
-
- const std::vector<OptionValue> &streamParameters = values.toArray();
-
- StreamRoles roles;
- for (auto const &value : streamParameters) {
- StreamRole role;
-
- /* If role is invalid or not set default to viewfinder. */
- if (!parseRole(&role, value.toKeyValues()))
- role = StreamRole::Viewfinder;
-
- roles.push_back(role);
- }
-
- return roles;
-}
-
-int StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,
- const OptionValue &values)
-{
- if (!config) {
- std::cerr << "No configuration provided" << std::endl;
- return -EINVAL;
- }
-
- /* If no configuration values nothing to do. */
- if (values.empty())
- return 0;
-
- const std::vector<OptionValue> &streamParameters = values.toArray();
-
- if (config->size() != streamParameters.size()) {
- std::cerr
- << "Number of streams in configuration "
- << config->size()
- << " does not match number of streams parsed "
- << streamParameters.size()
- << std::endl;
- return -EINVAL;
- }
-
- unsigned int i = 0;
- for (auto const &value : streamParameters) {
- KeyValueParser::Options opts = value.toKeyValues();
- StreamConfiguration &cfg = config->at(i++);
-
- if (opts.isSet("width") && opts.isSet("height")) {
- cfg.size.width = opts["width"];
- cfg.size.height = opts["height"];
- }
-
- if (opts.isSet("pixelformat"))
- cfg.pixelFormat = PixelFormat::fromString(opts["pixelformat"].toString());
-
- if (opts.isSet("colorspace"))
- cfg.colorSpace = ColorSpace::fromString(opts["colorspace"].toString());
- }
-
- return 0;
-}
-
-bool StreamKeyValueParser::parseRole(StreamRole *role,
- const KeyValueParser::Options &options)
-{
- if (!options.isSet("role"))
- return false;
-
- std::string name = options["role"].toString();
-
- if (name == "viewfinder") {
- *role = StreamRole::Viewfinder;
- return true;
- } else if (name == "video") {
- *role = StreamRole::VideoRecording;
- return true;
- } else if (name == "still") {
- *role = StreamRole::StillCapture;
- return true;
- } else if (name == "raw") {
- *role = StreamRole::Raw;
- return true;
- }
-
- return false;
-}
diff --git a/src/apps/cam/stream_options.h b/src/apps/cam/stream_options.h
deleted file mode 100644
index 35e4e7c0..00000000
--- a/src/apps/cam/stream_options.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2020, Raspberry Pi Ltd
- *
- * stream_options.h - Helper to parse options for streams
- */
-
-#pragma once
-
-#include <libcamera/camera.h>
-
-#include "options.h"
-
-class StreamKeyValueParser : public KeyValueParser
-{
-public:
- StreamKeyValueParser();
-
- KeyValueParser::Options parse(const char *arguments) override;
-
- static libcamera::StreamRoles roles(const OptionValue &values);
- static int updateConfiguration(libcamera::CameraConfiguration *config,
- const OptionValue &values);
-
-private:
- static bool parseRole(libcamera::StreamRole *role,
- const KeyValueParser::Options &options);
-};