Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qavfcamera.mm
Go to the documentation of this file.
1// Copyright (C) 2022 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#include <qavfcamera_p.h>
4#include <qpointer.h>
6#include <private/qplatformmediacapture_p.h>
8#include "qavfhelpers_p.h"
9#include "avfcameradebug_p.h"
11#include <qvideosink.h>
12#include <rhi/qrhi.h>
13#include <QtCore/qcoreapplication.h>
14#include <QtCore/qpermissions.h>
15#define AVMediaType XAVMediaType
17#include "qffmpegvideosink_p.h"
18extern "C" {
19#include <libavutil/hwcontext_videotoolbox.h>
20#include <libavutil/hwcontext.h>
21}
22#undef AVMediaType
23
25
26using namespace QFFmpeg;
27
29 : QAVFCameraBase(parent)
30{
31 m_captureSession = [[AVCaptureSession alloc] init];
32 m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc]
33 initWithFrameHandler:[this](const QVideoFrame &frame) { syncHandleFrame(frame); }];
34}
35
37{
38 [m_sampleBufferDelegate release];
39 [m_videoInput release];
40 [m_videoDataOutput release];
41 [m_captureSession release];
42}
43
44bool QAVFCamera::checkCameraPermission()
45{
46 const QCameraPermission permission;
47 const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
48 if (!granted)
49 qWarning() << "Access to camera not granted";
50
51 return granted;
52}
53
54void QAVFCamera::updateVideoInput()
55{
56 if (!checkCameraPermission())
57 return;
58
59 [m_captureSession beginConfiguration];
60
61 attachVideoInputDevice();
62
63 if (!m_videoDataOutput) {
64 m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
65
66 // Configure video output
67 m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
68 [m_videoDataOutput
69 setSampleBufferDelegate:m_sampleBufferDelegate
70 queue:m_delegateQueue];
71
72 [m_captureSession addOutput:m_videoDataOutput];
73 }
74 [m_captureSession commitConfiguration];
76}
77
79{
80 AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
81 if (connection == nil || !m_videoDataOutput)
82 return;
83
84 if (!connection.supportsVideoOrientation)
85 return;
86
87 if (angle < 0)
88 angle = m_orientationHandler.currentOrientation();
89
90 AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait;
91 switch (angle) {
92 default:
93 break;
94 case 90:
95 orientation = AVCaptureVideoOrientationLandscapeRight;
96 break;
97 case 180:
98 // this keeps the last orientation, don't do anything
99 return;
100 case 270:
101 orientation = AVCaptureVideoOrientationLandscapeLeft;
102 break;
103 }
104
105 connection.videoOrientation = orientation;
106}
107
108void QAVFCamera::attachVideoInputDevice()
109{
110 if (m_videoInput) {
111 [m_captureSession removeInput:m_videoInput];
112 [m_videoInput release];
113 m_videoInput = nullptr;
114 }
115
116 QByteArray deviceId = m_cameraDevice.id();
117 if (deviceId.isEmpty())
118 return;
119
120 AVCaptureDevice *videoDevice = [AVCaptureDevice deviceWithUniqueID:
121 [NSString stringWithUTF8String: deviceId.constData()]];
122
123 if (!videoDevice)
124 return;
125
126 m_videoInput = [AVCaptureDeviceInput
127 deviceInputWithDevice:videoDevice
128 error:nil];
129 if (m_videoInput && [m_captureSession canAddInput:m_videoInput]) {
130 [m_videoInput retain];
131 [m_captureSession addInput:m_videoInput];
132 } else {
133 qWarning() << "Failed to create video device input";
134 }
135}
136
137AVCaptureDevice *QAVFCamera::device() const
138{
139 return m_videoInput ? m_videoInput.device : nullptr;
140}
141
143{
144 return m_active;
145}
146
147void QAVFCamera::setActive(bool active)
148{
149 if (m_active == active)
150 return;
151 if (!checkCameraPermission())
152 return;
153
154 m_active = active;
155
156 if (active) {
157 // According to the doc, the capture device must be locked before
158 // startRunning to prevent the format we set to be overridden by the
159 // session preset.
160 [m_videoInput.device lockForConfiguration:nil];
161 [m_captureSession startRunning];
162 [m_videoInput.device unlockForConfiguration];
163 } else {
164 [m_captureSession stopRunning];
165 }
166
167 emit activeChanged(active);
168}
169
171{
172 m_session = session ? session->captureSession() : nullptr;
173}
174
176{
177 if (m_cameraDevice == camera)
178 return;
179
181
182 if (checkCameraPermission())
183 updateVideoInput();
184 setCameraFormat({});
185}
186
188{
189 if (m_cameraFormat == format && !format.isNull())
190 return true;
191
193 return false;
194
195 updateCameraFormat();
196 return true;
197}
198
199void QAVFCamera::updateCameraFormat()
200{
202
203 AVCaptureDevice *captureDevice = device();
204 if (!captureDevice)
205 return;
206
207 AVCaptureDeviceFormat *newFormat = qt_convert_to_capture_device_format(
208 captureDevice, m_cameraFormat, &isCVFormatSupported);
209
210 if (!newFormat)
211 newFormat = qt_convert_to_capture_device_format(captureDevice, m_cameraFormat);
212
213 std::uint32_t cvPixelFormat = 0;
214 if (newFormat) {
215 qt_set_active_format(captureDevice, newFormat, false);
216 const auto captureDeviceCVFormat =
217 CMVideoFormatDescriptionGetCodecType(newFormat.formatDescription);
218 cvPixelFormat = setPixelFormat(m_cameraFormat.pixelFormat(), captureDeviceCVFormat);
219 if (captureDeviceCVFormat != cvPixelFormat) {
220 qCWarning(qLcCamera) << "Output CV format differs with capture device format!"
221 << cvPixelFormat << cvFormatToString(cvPixelFormat) << "vs"
222 << captureDeviceCVFormat
223 << cvFormatToString(captureDeviceCVFormat);
224
226 }
227 } else {
228 qWarning() << "Cannot find AVCaptureDeviceFormat; Did you use format from another camera?";
229 }
230
231 const AVPixelFormat avPixelFormat = av_map_videotoolbox_format_to_pixfmt(cvPixelFormat);
232
233 std::unique_ptr<HWAccel> hwAccel;
234
235 if (avPixelFormat == AV_PIX_FMT_NONE) {
236 qCWarning(qLcCamera) << "Videotoolbox doesn't support cvPixelFormat:" << cvPixelFormat
237 << cvFormatToString(cvPixelFormat)
238 << "Camera pix format:" << m_cameraFormat.pixelFormat();
239 } else {
240 hwAccel = HWAccel::create(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
241 qCDebug(qLcCamera) << "Create VIDEOTOOLBOX hw context" << hwAccel.get() << "for camera";
242 }
243
244 if (hwAccel) {
245 hwAccel->createFramesContext(avPixelFormat, adjustedResolution());
246 m_hwPixelFormat = hwAccel->hwFormat();
247 } else {
248 m_hwPixelFormat = AV_PIX_FMT_NONE;
249 }
250
251 [m_sampleBufferDelegate setHWAccel:std::move(hwAccel)];
252 [m_sampleBufferDelegate setVideoFormatFrameRate:m_cameraFormat.maxFrameRate()];
253}
254
255uint32_t QAVFCamera::setPixelFormat(QVideoFrameFormat::PixelFormat cameraPixelFormat,
256 uint32_t inputCvPixFormat)
257{
258 auto bestScore = MinAVScore;
259 NSNumber *bestFormat = nullptr;
260 for (NSNumber *cvPixFmtNumber in m_videoDataOutput.availableVideoCVPixelFormatTypes) {
261 auto cvPixFmt = [cvPixFmtNumber unsignedIntValue];
262 const auto pixFmt = QAVFHelpers::fromCVPixelFormat(cvPixFmt);
264 continue;
265
266 auto score = DefaultAVScore;
267 if (cvPixFmt == inputCvPixFormat)
268 score += 100;
269 if (pixFmt == cameraPixelFormat)
270 score += 10;
271 // if (cvPixFmt == kCVPixelFormatType_32BGRA)
272 // score += 1;
273
274 // This flag determines priorities of using ffmpeg hw frames or
275 // the exact camera format match.
276 // Maybe configure more, e.g. by some env var?
277 constexpr bool ShouldSuppressNotSupportedByFFmpeg = false;
278
279 if (!isCVFormatSupported(cvPixFmt))
280 score -= ShouldSuppressNotSupportedByFFmpeg ? 100000 : 5;
281
282 // qDebug() << "----FMT:" << pixFmt << cvPixFmt << score;
283
284 if (score > bestScore) {
285 bestScore = score;
286 bestFormat = cvPixFmtNumber;
287 }
288 }
289
290 if (!bestFormat) {
291 qWarning() << "QCamera::setCameraFormat: availableVideoCVPixelFormatTypes empty";
292 return 0;
293 }
294
295 if (bestScore < DefaultAVScore)
296 qWarning() << "QCamera::setCameraFormat: Cannot find hw FFmpeg supported cv pix format";
297
298 NSDictionary *outputSettings = @{
299 (NSString *)kCVPixelBufferPixelFormatTypeKey : bestFormat,
300 (NSString *)kCVPixelBufferMetalCompatibilityKey : @true
301 };
302 m_videoDataOutput.videoSettings = outputSettings;
303
304 return [bestFormat unsignedIntValue];
305}
306
307QSize QAVFCamera::adjustedResolution() const
308{
309 // Check, that we have matching dimesnions.
310 QSize resolution = m_cameraFormat.resolution();
311 AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
312 if (!connection.supportsVideoOrientation)
313 return resolution;
314
315 // Either portrait but actually sizes of landscape, or
316 // landscape with dimensions of portrait - not what
317 // sample delegate will report (it depends on videoOrientation set).
318 const bool isPortraitOrientation = connection.videoOrientation == AVCaptureVideoOrientationPortrait;
319 const bool isPortraitResolution = resolution.height() > resolution.width();
320 if (isPortraitOrientation != isPortraitResolution)
321 resolution.transpose();
322
323 return resolution;
324}
325
330
331std::optional<int> QAVFCamera::ffmpegHWPixelFormat() const
332{
333 return m_hwPixelFormat == AV_PIX_FMT_NONE ? std::optional<int>{} : m_hwPixelFormat;
334}
335
337 QVideoFrameFormat::ColorRange colorRange) const
338{
339 auto cvFormat = QAVFHelpers::toCVPixelFormat(pixelFormat, colorRange);
340 return static_cast<int>(isCVFormatSupported(cvFormat));
341}
342
344
345#include "moc_qavfcamera_p.cpp"
bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps)
AVCaptureDeviceFormat * qt_convert_to_capture_device_format(AVCaptureDevice *captureDevice, const QCameraFormat &cameraFormat, const std::function< bool(uint32_t)> &cvFormatValidator)
QCameraDevice m_cameraDevice
bool setCameraFormat(const QCameraFormat &format) override
void setCaptureSession(QPlatformMediaCaptureSession *) override
void setCamera(const QCameraDevice &camera) override
void deviceOrientationChanged(int angle=-1)
Definition qavfcamera.mm:78
int cameraPixelFormatScore(QVideoFrameFormat::PixelFormat pixelFmt, QVideoFrameFormat::ColorRange colorRange) const override
void setActive(bool active) override
void syncHandleFrame(const QVideoFrame &frame)
bool setCameraFormat(const QCameraFormat &format) override
QAVFCamera(QCamera *parent)
Definition qavfcamera.mm:28
bool isActive() const override
std::optional< int > ffmpegHWPixelFormat() const override
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
The QCameraDevice class provides general information about camera devices.
QByteArray id
\qmlproperty string QtMultimedia::cameraDevice::id
The QCameraFormat class describes a video format supported by a camera device. \inmodule QtMultimedia...
QSize resolution
\qmlproperty size QtMultimedia::cameraFormat::resolution
bool isNull() const noexcept
Returns true if this is a default constructed QCameraFormat.
QVideoFrameFormat::PixelFormat pixelFormat
\qmlproperty enumeration QtMultimedia::cameraFormat::pixelFormat
float maxFrameRate
\qmlproperty real QtMultimedia::cameraFormat::maxFrameRate
Access the camera for taking pictures or videos.
The QCamera class provides interface for system camera devices.
Definition qcamera.h:28
static std::unique_ptr< HWAccel > create(AVHWDeviceType deviceType)
QVideoFrameFormat::PixelFormat m_framePixelFormat
QCamera::Error error() const
QCameraFormat m_cameraFormat
QMediaCaptureSession * captureSession() const
void newVideoFrame(const QVideoFrame &)
void activeChanged(bool)
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
void transpose() noexcept
Swaps the width and height values.
Definition qsize.cpp:130
PixelFormat
Enumerates video data types.
ColorRange
Describes the color range used by the video data.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
QCamera * camera
Definition camera.cpp:19
QVideoFrameFormat::PixelFormat fromCVPixelFormat(CvPixelFormat cvPixelFormat)
CvPixelFormat toCVPixelFormat(QVideoFrameFormat::PixelFormat pixFmt, QVideoFrameFormat::ColorRange colorRange)
constexpr AVScore DefaultAVScore
Definition qffmpeg_p.h:160
constexpr AVScore MinAVScore
Definition qffmpeg_p.h:162
Combined button and popup list for selecting options.
#define qApp
DBusConnection * connection
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
#define qCDebug(category,...)
GLfloat angle
GLint GLsizei GLsizei GLenum format
GLuint in
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_EMIT
#define emit
sem release()
QQueue< int > queue
[0]
QFrame frame
[0]