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>
18[[nodiscard]] QCameraDevice::Position qAvfToQCameraDevicePosition(AVCaptureDevicePosition input)
21 case AVCaptureDevicePositionFront:
22 return QCameraDevice::Position::FrontFace;
23 case AVCaptureDevicePositionBack:
24 return QCameraDevice::Position::BackFace;
26 return QCameraDevice::Position::UnspecifiedPosition;
31[[nodiscard]] QList<AVCaptureDevice*> qEnumerateAVCaptureDevices()
38 NSArray *discoveryDevices = @[
40 AVCaptureDeviceTypeBuiltInTripleCamera,
41 AVCaptureDeviceTypeBuiltInDualCamera,
43 AVCaptureDeviceTypeBuiltInDualWideCamera,
46 AVCaptureDeviceTypeBuiltInWideAngleCamera,
49 AVCaptureDeviceTypeBuiltInTelephotoCamera,
51 AVCaptureDeviceTypeBuiltInUltraWideCamera,
55 if (@available(macOS 14, iOS 17, *)) {
56 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
57 AVCaptureDeviceTypeExternal,
58 AVCaptureDeviceTypeContinuityCamera
63 QT_WARNING_DISABLE_DEPRECATED
64 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
65 AVCaptureDeviceTypeExternalUnknown
72 AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
73 discoverySessionWithDeviceTypes:discoveryDevices
74 mediaType:AVMediaTypeVideo
75 position:AVCaptureDevicePositionUnspecified];
76 QList<AVCaptureDevice*> avCaptureDevices;
77 for (AVCaptureDevice* device in discoverySession.devices)
78 avCaptureDevices.push_back(device);
79 return avCaptureDevices;
85[[nodiscard]] QList<QCameraDevice> qGenerateQCameraDevices(
86 QList<AVCaptureDevice*> videoDevices,
87 const std::function<
bool(CvPixelFormat)>& isCvPixelFormatSupported)
89 QList<QCameraDevice> cameras;
91 for (AVCaptureDevice *device : videoDevices) {
92 if ([device isSuspended])
95 auto info = std::make_unique<QCameraDevicePrivate>();
96 if ([videoDevices[0].uniqueID isEqualToString:device.uniqueID])
97 info->isDefault =
true;
98 info->id = QByteArray([[device uniqueID] UTF8String]);
99 info->description = QString::fromNSString([device localizedName]);
100 info->position = qAvfToQCameraDevicePosition([device position]);
102 qCDebug(qLcAvfVideoDevices) <<
"Handling camera info" << info->description
103 << (info->isDefault ?
"(default)" :
"");
105 QSet<QSize> photoResolutions;
106 QList<QCameraFormat> videoFormats;
108 for (AVCaptureDeviceFormat *format in device.formats) {
109 if (![format.mediaType isEqualToString:AVMediaTypeVideo])
112 const CMVideoDimensions dimensions =
113 CMVideoFormatDescriptionGetDimensions(format.formatDescription);
114 QSize resolution(dimensions.width, dimensions.height);
115 photoResolutions.insert(resolution);
117 float maxFrameRate = 0;
118 float minFrameRate = 1.e6;
120 const CvPixelFormat cvPixelFormat =
121 CMVideoFormatDescriptionGetCodecType(format.formatDescription);
125 if (!isCvPixelFormatSupported(cvPixelFormat))
128 const QVideoFrameFormat::PixelFormat pixelFormat =
129 QAVFHelpers::fromCVPixelFormat(cvPixelFormat);
130 const QVideoFrameFormat::ColorRange colorRange =
131 QAVFHelpers::colorRangeForCVPixelFormat(cvPixelFormat);
134 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
135 qCDebug(qLcAvfVideoDevices) <<
"ignore camera CV format" << cvPixelFormat
136 <<
"as no matching video format found";
140 for (
const AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
141 if (frameRateRange.minFrameRate < minFrameRate)
142 minFrameRate = frameRateRange.minFrameRate;
143 if (frameRateRange.maxFrameRate > maxFrameRate)
144 maxFrameRate = frameRateRange.maxFrameRate;
153 const QSize hrRes(qt_device_format_high_resolution(format));
154 if (!hrRes.isNull() && hrRes.isValid())
155 photoResolutions.insert(hrRes);
158 qCDebug(qLcAvfVideoDevices) <<
"Add camera format. pixelFormat:" << pixelFormat
159 <<
"colorRange:" << colorRange <<
"cvPixelFormat" << cvPixelFormat
160 <<
"resolution:" << resolution <<
"frameRate: [" << minFrameRate
161 << maxFrameRate <<
"]";
163 auto *f =
new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution,
164 minFrameRate, maxFrameRate, colorRange };
165 videoFormats << f->create();
167 if (videoFormats.isEmpty()) {
169 qCWarning(qLcAvfVideoDevices())
170 <<
"Skip camera" << info->description <<
"without supported formats";
173 info->videoFormats = videoFormats;
174 info->photoResolutions = photoResolutions.values();
176 cameras.append(info.release()->create());
184QAVFVideoDevices::QAVFVideoDevices(
185 QPlatformMediaIntegration *integration,
186 std::function<
bool(uint32_t)> &&isCvPixelFormatSupportedDelegate)
187 : QPlatformVideoDevices(integration),
188 m_isCvPixelFormatSupportedDelegate(std::move(isCvPixelFormatSupportedDelegate))
190 m_deviceConnectedObserver = QMacNotificationObserver(
192 AVCaptureDeviceWasConnectedNotification,
197 QMetaObject::invokeMethod(
200 rebuildObserveredAvCaptureDevices();
201 QPlatformVideoDevices::onVideoInputsChanged();
205 m_deviceDisconnectedObserver = QMacNotificationObserver(
207 AVCaptureDeviceWasDisconnectedNotification,
209 QMetaObject::invokeMethod(
212 rebuildObserveredAvCaptureDevices();
213 QPlatformVideoDevices::onVideoInputsChanged();
217 rebuildObserveredAvCaptureDevices();
220QAVFVideoDevices::~QAVFVideoDevices()
222 clearObservedAvCaptureDevices();
226void QAVFVideoDevices::clearObservedAvCaptureDevices() {
227 for (ObservedAVCaptureDevice& observedDevice : m_observedAvCaptureDevices) {
229 observedDevice.observer = {};
230 [observedDevice.avCaptureDevice release];
232 m_observedAvCaptureDevices.clear();
236QList<QCameraDevice> QAVFVideoDevices::findVideoInputs()
const
238 const QList<AVCaptureDevice*> avCaptureDevices = qEnumerateAVCaptureDevices();
239 return qGenerateQCameraDevices(
241 [
this](uint32_t cvPixelFormat) {
242 return isCvPixelFormatSupported(cvPixelFormat);
246bool QAVFVideoDevices::isCvPixelFormatSupported(uint32_t cvPixelFormat)
const
248 return !m_isCvPixelFormatSupportedDelegate || m_isCvPixelFormatSupportedDelegate(cvPixelFormat);
253void QAVFVideoDevices::rebuildObserveredAvCaptureDevices()
255 const QList<AVCaptureDevice*> avCaptureDevices = qEnumerateAVCaptureDevices();
257 clearObservedAvCaptureDevices();
259 for (AVCaptureDevice *captureDevice : avCaptureDevices) {
260 ObservedAVCaptureDevice observedDevice;
262 observedDevice.avCaptureDevice = captureDevice;
263 [observedDevice.avCaptureDevice retain];
267 observedDevice.observer = QMacKeyValueObserver(
268 observedDevice.avCaptureDevice,
273 QMetaObject::invokeMethod(
276 rebuildObserveredAvCaptureDevices();
277 QPlatformVideoDevices::onVideoInputsChanged();
281 m_observedAvCaptureDevices.push_back(std::move(observedDevice));
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")