4#include <QtMultimedia/private/qavfvideodevices_p.h>
5#include <QtMultimedia/private/qcameradevice_p.h>
6#include <QtMultimedia/private/qavfhelpers_p.h>
7#include <QtMultimedia/private/qavfcamerautility_p.h>
9#include <QtCore/qloggingcategory.h>
10#include <QtCore/qset.h>
11#include <QtCore/qspan.h>
12#include <QtCore/qthread.h>
22[[nodiscard]] QCameraDevice::Position qAvfToQCameraDevicePosition(AVCaptureDevicePosition input)
25 case AVCaptureDevicePositionFront:
26 return QCameraDevice::Position::FrontFace;
27 case AVCaptureDevicePositionBack:
28 return QCameraDevice::Position::BackFace;
30 return QCameraDevice::Position::UnspecifiedPosition;
35[[nodiscard]] std::vector<AVCaptureDevice *> qEnumerateAVCaptureDevices()
42 NSArray *discoveryDevices = @[
44 AVCaptureDeviceTypeBuiltInTripleCamera,
45 AVCaptureDeviceTypeBuiltInDualCamera,
47 AVCaptureDeviceTypeBuiltInDualWideCamera,
50 AVCaptureDeviceTypeBuiltInWideAngleCamera,
53 AVCaptureDeviceTypeBuiltInTelephotoCamera,
55 AVCaptureDeviceTypeBuiltInUltraWideCamera,
59 if (@available(macOS 14, *)) {
60 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
61 AVCaptureDeviceTypeExternal,
62 AVCaptureDeviceTypeContinuityCamera
67 QT_WARNING_DISABLE_DEPRECATED
68 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
69 AVCaptureDeviceTypeExternalUnknown
76 AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
77 discoverySessionWithDeviceTypes:discoveryDevices
78 mediaType:AVMediaTypeVideo
79 position:AVCaptureDevicePositionUnspecified];
80 std::vector<AVCaptureDevice *> avCaptureDevices;
81 avCaptureDevices.reserve(discoverySession.devices.count);
82 for (AVCaptureDevice *device in discoverySession.devices)
83 avCaptureDevices.push_back(device);
84 return avCaptureDevices;
90[[nodiscard]] QList<QCameraDevice> qGenerateQCameraDevices(
91 QSpan<
const AVCaptureDevice *
const> videoDevices,
92 const std::function<
bool(CvPixelFormat)>& isCvPixelFormatSupported)
94 QList<QCameraDevice> cameras;
96 for (
const AVCaptureDevice *device : videoDevices) {
97 if ([device isSuspended])
100 auto info = std::make_unique<QCameraDevicePrivate>();
101 if ([videoDevices[0].uniqueID isEqualToString:device.uniqueID])
102 info->isDefault =
true;
103 info->id = QByteArray([[device uniqueID] UTF8String]);
104 info->description = QString::fromNSString([device localizedName]);
105 info->position = qAvfToQCameraDevicePosition([device position]);
107 qCDebug(qLcAvfVideoDevices) <<
"Handling camera info" << info->description
108 << (info->isDefault ?
"(default)" :
"");
110 QSet<QSize> photoResolutions;
111 QList<QCameraFormat> videoFormats;
113 for (AVCaptureDeviceFormat *format in device.formats) {
114 if (![format.mediaType isEqualToString:AVMediaTypeVideo])
117 const CMVideoDimensions dimensions =
118 CMVideoFormatDescriptionGetDimensions(format.formatDescription);
119 QSize resolution(dimensions.width, dimensions.height);
120 photoResolutions.insert(resolution);
122 float maxFrameRate = 0;
123 float minFrameRate = 1.e6;
125 const CvPixelFormat cvPixelFormat =
126 CMVideoFormatDescriptionGetCodecType(format.formatDescription);
130 if (!isCvPixelFormatSupported(cvPixelFormat))
133 const QVideoFrameFormat::PixelFormat pixelFormat =
134 QAVFHelpers::fromCVPixelFormat(cvPixelFormat);
135 const QVideoFrameFormat::ColorRange colorRange =
136 QAVFHelpers::colorRangeForCVPixelFormat(cvPixelFormat);
139 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
140 qCDebug(qLcAvfVideoDevices) <<
"ignore camera CV format" << cvPixelFormat
141 <<
"as no matching video format found";
145 for (
const AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
146 if (frameRateRange.minFrameRate < minFrameRate)
147 minFrameRate = frameRateRange.minFrameRate;
148 if (frameRateRange.maxFrameRate > maxFrameRate)
149 maxFrameRate = frameRateRange.maxFrameRate;
158 const QSize hrRes(qt_device_format_high_resolution(format));
159 if (!hrRes.isNull() && hrRes.isValid())
160 photoResolutions.insert(hrRes);
163 qCDebug(qLcAvfVideoDevices) <<
"Add camera format. pixelFormat:" << pixelFormat
164 <<
"colorRange:" << colorRange <<
"cvPixelFormat" << cvPixelFormat
165 <<
"resolution:" << resolution <<
"frameRate: [" << minFrameRate
166 << maxFrameRate <<
"]";
168 auto *f =
new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution,
169 minFrameRate, maxFrameRate, colorRange };
170 videoFormats << f->create();
172 if (videoFormats.isEmpty()) {
174 qCWarning(qLcAvfVideoDevices())
175 <<
"Skip camera" << info->description <<
"without supported formats";
178 info->videoFormats = videoFormats;
179 info->photoResolutions = photoResolutions.values();
181 cameras.append(info.release()->create());
189QAVFVideoDevices::QAVFVideoDevices(
190 QPlatformMediaIntegration *integration,
191 std::function<
bool(uint32_t)> &&isCvPixelFormatSupportedDelegate)
192 : QPlatformVideoDevices(integration),
193 m_isCvPixelFormatSupportedDelegate(std::move(isCvPixelFormatSupportedDelegate))
195 m_deviceConnectedObserver = QMacNotificationObserver(
197 AVCaptureDeviceWasConnectedNotification,
202 QMetaObject::invokeMethod(
205 rebuildObserveredAvCaptureDevices();
206 QPlatformVideoDevices::onVideoInputsChanged();
210 m_deviceDisconnectedObserver = QMacNotificationObserver(
212 AVCaptureDeviceWasDisconnectedNotification,
214 QMetaObject::invokeMethod(
217 rebuildObserveredAvCaptureDevices();
218 QPlatformVideoDevices::onVideoInputsChanged();
222 rebuildObserveredAvCaptureDevices();
225QAVFVideoDevices::~QAVFVideoDevices() =
default;
228void QAVFVideoDevices::clearObservedAvCaptureDevices()
230 Q_ASSERT(thread()->isCurrentThread());
231 m_observedAvCaptureDevices.clear();
235QList<QCameraDevice> QAVFVideoDevices::findVideoInputs()
const
237 std::vector<AVCaptureDevice *> avCaptureDevices = qEnumerateAVCaptureDevices();
238 return qGenerateQCameraDevices(
240 [
this](uint32_t cvPixelFormat) {
241 return isCvPixelFormatSupported(cvPixelFormat);
245bool QAVFVideoDevices::isCvPixelFormatSupported(uint32_t cvPixelFormat)
const
247 return !m_isCvPixelFormatSupportedDelegate || m_isCvPixelFormatSupportedDelegate(cvPixelFormat);
251void QAVFVideoDevices::rebuildObserveredAvCaptureDevices()
253 Q_ASSERT(thread()->isCurrentThread());
255 std::vector<AVCaptureDevice *> avCaptureDevices = qEnumerateAVCaptureDevices();
257 clearObservedAvCaptureDevices();
259 for (AVCaptureDevice *captureDevice : avCaptureDevices) {
260 ObservedAVCaptureDevice observedDevice;
261 observedDevice.avCaptureDevice = AVFScopedPointer{ [captureDevice retain] };
265 observedDevice.observer = QMacKeyValueObserver(
266 observedDevice.avCaptureDevice,
271 QMetaObject::invokeMethod(
274 rebuildObserveredAvCaptureDevices();
275 QPlatformVideoDevices::onVideoInputsChanged();
279 m_observedAvCaptureDevices.push_back(std::move(observedDevice));
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)