Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlibcamera_format_support.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include <QtMultimedia/private/qmultimedia_drm_support_p.h>
8#include <QtMultimedia/private/qmultimedia_ranges_p.h>
9
10#include <libcamera/base/span.h>
11#include <libcamera/color_space.h>
12#include <libcamera/control_ids.h>
13#include <libcamera/controls.h>
14#include <libcamera/geometry.h>
15#include <libcamera/pixel_format.h>
16#include <libcamera/stream.h>
17
18#include <algorithm>
19
20QT_BEGIN_NAMESPACE
21
22namespace QLibCamera {
23
24using namespace libcamera;
25
27
29{
30 using namespace QtMultimediaPrivate;
31 return pixelFormatFromDrmFourcc(DRMFormat(format.fourcc()));
32}
33
35colorSpaceFromLibcamera(const std::optional<ColorSpace> &colorSpace)
36{
37 if (!colorSpace)
38 return { QVideoFrameFormat::ColorSpace_Undefined, QVideoFrameFormat::ColorRange_Unknown };
39
40 QVideoFrameFormat::ColorSpace space = QVideoFrameFormat::ColorSpace_Undefined;
41 switch (colorSpace->ycbcrEncoding) {
42 case ColorSpace::YcbcrEncoding::Rec601:
43 space = QVideoFrameFormat::ColorSpace_BT601;
44 break;
45 case ColorSpace::YcbcrEncoding::Rec709:
46 space = QVideoFrameFormat::ColorSpace_BT709;
47 break;
48 case ColorSpace::YcbcrEncoding::Rec2020:
49 space = QVideoFrameFormat::ColorSpace_BT2020;
50 break;
51 case ColorSpace::YcbcrEncoding::None:
52 // RGB colorspace (e.g. sRGB) rather than YCbCr; fall back to the primaries.
53 if (colorSpace->primaries == ColorSpace::Primaries::Rec2020)
54 space = QVideoFrameFormat::ColorSpace_BT2020;
55 break;
56 }
57
58 const QVideoFrameFormat::ColorRange range = colorSpace->range == ColorSpace::Range::Full
59 ? QVideoFrameFormat::ColorRange_Full
60 : QVideoFrameFormat::ColorRange_Video;
61
62 return { space, range };
63}
64
65std::pair<float, float> frameRateRangeFromControls(const ControlInfoMap &controls)
66{
67 constexpr std::pair<float, float> fallbackRange{ 1.f, 30.f };
68
69 const auto it = controls.find(&libcamera::controls::FrameDurationLimits);
70 if (it == controls.end()) {
71 qCDebug(lcLibCamera) << "FrameDurationLimits not advertised pre-acquire(); "
72 "falling back to a 1-30 fps range";
73 return fallbackRange;
74 }
75
76 const ControlValue &min = it->second.min();
77 const ControlValue &max = it->second.max();
78 // FrameDurationLimits is declared as Control<Span<const int64_t, 2>>, but not every pipeline
79 // handler actually populates ControlInfo::min()/max() as an array value of that shape (e.g.
80 // libcamera's own "virtual" test pipeline handler does not, as of libcamera 0.7.0) --
81 // ControlValue::get<Span<...>>() asserts isArray_ internally and aborts the process if that
82 // assumption is wrong, so it must never be called without checking isArray() first.
83 if (min.isNone() || max.isNone() || !min.isArray() || !max.isArray()) {
84 qCDebug(lcLibCamera) << "FrameDurationLimits is not an array-typed min/max control; "
85 "falling back to a 1-30 fps range";
86 return fallbackRange;
87 }
88
89 // FrameDurationLimits is { minDuration, maxDuration } in microseconds; convert to fps and
90 // note that the *maximum* duration corresponds to the *minimum* frame rate and vice versa.
91 const auto durations = min.get<Span<const int64_t, 2>>();
92 const auto maxDurations = max.get<Span<const int64_t, 2>>();
93 if (durations.size() < 1 || maxDurations.size() < 1)
94 return fallbackRange;
95
96 const int64_t minDurationUs = durations[0];
97 const int64_t maxDurationUs = maxDurations[0];
98 if (minDurationUs <= 0 || maxDurationUs <= 0)
99 return fallbackRange;
100
101 const float maxFrameRate = 1'000'000.f / float(minDurationUs);
102 const float minFrameRate = 1'000'000.f / float(maxDurationUs);
103 return { minFrameRate, maxFrameRate };
104}
105
106std::vector<QSize> sizesForPixelFormat(const StreamFormats &formats, const PixelFormat &pixelFormat)
107{
108 namespace ranges = QtMultimediaPrivate::ranges;
109
110 std::vector<QSize> sizes;
111 for (const Size &size : formats.sizes(pixelFormat))
112 sizes.push_back(toQSize(size));
113
114 // sizes() only intersects a fixed table of common resolutions against a continuous range, so
115 // it can miss the camera's true native extremes; range() always reports them (as a bounding
116 // box in the multi-entry/discrete case, or the range itself in the single-entry case), so
117 // union them in rather than relying on sizes() alone.
118 const SizeRange range = formats.range(pixelFormat);
119 if (!range.min.isNull() || !range.max.isNull()) {
120 sizes.push_back(toQSize(range.min));
121 if (range.min != range.max)
122 sizes.push_back(toQSize(range.max));
123 }
124
125 ranges::sort(sizes, [](const QSize &a, const QSize &b) {
126 return std::pair(a.width(), a.height()) < std::pair(b.width(), b.height());
127 });
128 sizes.erase(std::unique(sizes.begin(), sizes.end()), sizes.end());
129 return sizes;
130}
131
132QSize toQSize(const Size &size)
133{
134 return QSize{
135 int(size.width),
136 int(size.height),
137 };
138}
139
140Size toSize(const QSize &qsize)
141{
142 return Size{
143 unsigned(qsize.width()),
144 unsigned(qsize.height()),
145 };
146}
147
148} // namespace QLibCameraFormatSupport
149
150} // namespace QLibCamera
151
152QT_END_NAMESPACE
std::pair< float, float > frameRateRangeFromControls(const ControlInfoMap &controls)
std::optional< QVideoFrameFormat::PixelFormat > pixelFormatFromLibcamera(const PixelFormat &format)
std::pair< QVideoFrameFormat::ColorSpace, QVideoFrameFormat::ColorRange > colorSpaceFromLibcamera(const std::optional< ColorSpace > &colorSpace)
std::vector< QSize > sizesForPixelFormat(const StreamFormats &formats, const PixelFormat &pixelFormat)