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>
12#import <AVFoundation/AVFoundation.h>
20[[nodiscard]] QCameraDevice::Position qAvfToQCameraDevicePosition(AVCaptureDevicePosition input)
23 case AVCaptureDevicePositionFront:
24 return QCameraDevice::Position::FrontFace;
25 case AVCaptureDevicePositionBack:
26 return QCameraDevice::Position::BackFace;
28 return QCameraDevice::Position::UnspecifiedPosition;
33[[nodiscard]] QList<AVCaptureDevice*> qEnumerateAVCaptureDevices()
40 NSArray *discoveryDevices = @[
42 AVCaptureDeviceTypeBuiltInTripleCamera,
43 AVCaptureDeviceTypeBuiltInDualCamera,
45 AVCaptureDeviceTypeBuiltInDualWideCamera,
48 AVCaptureDeviceTypeBuiltInWideAngleCamera,
51 AVCaptureDeviceTypeBuiltInTelephotoCamera,
53 AVCaptureDeviceTypeBuiltInUltraWideCamera,
57 if (@available(macOS 14, iOS 17, *)) {
58 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
59 AVCaptureDeviceTypeExternal,
60 AVCaptureDeviceTypeContinuityCamera
65 QT_WARNING_DISABLE_DEPRECATED
66 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
67 AVCaptureDeviceTypeExternalUnknown
74 AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
75 discoverySessionWithDeviceTypes:discoveryDevices
76 mediaType:AVMediaTypeVideo
77 position:AVCaptureDevicePositionUnspecified];
78 QList<AVCaptureDevice*> avCaptureDevices;
79 for (AVCaptureDevice* device in discoverySession.devices)
80 avCaptureDevices.push_back(device);
81 return avCaptureDevices;
87[[nodiscard]] QList<QCameraDevice> qGenerateQCameraDevices(
88 QList<AVCaptureDevice*> videoDevices,
89 const std::function<
bool(CvPixelFormat)>& isCvPixelFormatSupported)
91 QList<QCameraDevice> cameras;
93 for (AVCaptureDevice *device : videoDevices) {
94 if ([device isSuspended])
97 auto info = std::make_unique<QCameraDevicePrivate>();
98 if ([videoDevices[0].uniqueID isEqualToString:device.uniqueID])
99 info->isDefault =
true;
100 info->id = QByteArray([[device uniqueID] UTF8String]);
101 info->description = QString::fromNSString([device localizedName]);
102 info->position = qAvfToQCameraDevicePosition([device position]);
104 qCDebug(qLcAvfVideoDevices) <<
"Handling camera info" << info->description
105 << (info->isDefault ?
"(default)" :
"");
107 QSet<QSize> photoResolutions;
108 QList<QCameraFormat> videoFormats;
110 for (AVCaptureDeviceFormat *format in device.formats) {
111 if (![format.mediaType isEqualToString:AVMediaTypeVideo])
114 const CMVideoDimensions dimensions =
115 CMVideoFormatDescriptionGetDimensions(format.formatDescription);
116 QSize resolution(dimensions.width, dimensions.height);
117 photoResolutions.insert(resolution);
119 float maxFrameRate = 0;
120 float minFrameRate = 1.e6;
122 const CvPixelFormat cvPixelFormat =
123 CMVideoFormatDescriptionGetCodecType(format.formatDescription);
127 if (!isCvPixelFormatSupported(cvPixelFormat))
130 const QVideoFrameFormat::PixelFormat pixelFormat =
131 QAVFHelpers::fromCVPixelFormat(cvPixelFormat);
132 const QVideoFrameFormat::ColorRange colorRange =
133 QAVFHelpers::colorRangeForCVPixelFormat(cvPixelFormat);
136 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
137 qCDebug(qLcAvfVideoDevices) <<
"ignore camera CV format" << cvPixelFormat
138 <<
"as no matching video format found";
142 for (
const AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
143 if (frameRateRange.minFrameRate < minFrameRate)
144 minFrameRate = frameRateRange.minFrameRate;
145 if (frameRateRange.maxFrameRate > maxFrameRate)
146 maxFrameRate = frameRateRange.maxFrameRate;
155 const QSize hrRes(qt_device_format_high_resolution(format));
156 if (!hrRes.isNull() && hrRes.isValid())
157 photoResolutions.insert(hrRes);
160 qCDebug(qLcAvfVideoDevices) <<
"Add camera format. pixelFormat:" << pixelFormat
161 <<
"colorRange:" << colorRange <<
"cvPixelFormat" << cvPixelFormat
162 <<
"resolution:" << resolution <<
"frameRate: [" << minFrameRate
163 << maxFrameRate <<
"]";
165 auto *f =
new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution,
166 minFrameRate, maxFrameRate, colorRange };
167 videoFormats << f->create();
169 if (videoFormats.isEmpty()) {
171 qCWarning(qLcAvfVideoDevices())
172 <<
"Skip camera" << info->description <<
"without supported formats";
175 info->videoFormats = videoFormats;
176 info->photoResolutions = photoResolutions.values();
178 cameras.append(info.release()->create());
186QAVFVideoDevices::QAVFVideoDevices(
187 QPlatformMediaIntegration *integration,
188 std::function<
bool(uint32_t)> &&isCvPixelFormatSupportedDelegate)
189 : QPlatformVideoDevices(integration),
190 m_isCvPixelFormatSupportedDelegate(std::move(isCvPixelFormatSupportedDelegate))
192 m_deviceConnectedObserver = QMacNotificationObserver(
194 AVCaptureDeviceWasConnectedNotification,
199 QMetaObject::invokeMethod(
202 rebuildObserveredAvCaptureDevices();
203 QPlatformVideoDevices::onVideoInputsChanged();
207 m_deviceDisconnectedObserver = QMacNotificationObserver(
209 AVCaptureDeviceWasDisconnectedNotification,
211 QMetaObject::invokeMethod(
214 rebuildObserveredAvCaptureDevices();
215 QPlatformVideoDevices::onVideoInputsChanged();
219 rebuildObserveredAvCaptureDevices();
222QAVFVideoDevices::~QAVFVideoDevices()
224 clearObservedAvCaptureDevices();
228void QAVFVideoDevices::clearObservedAvCaptureDevices() {
229 for (ObservedAVCaptureDevice& observedDevice : m_observedAvCaptureDevices) {
231 observedDevice.observer = {};
232 [observedDevice.avCaptureDevice release];
234 m_observedAvCaptureDevices.clear();
238QList<QCameraDevice> QAVFVideoDevices::findVideoInputs()
const
240 const QList<AVCaptureDevice*> avCaptureDevices = qEnumerateAVCaptureDevices();
241 return qGenerateQCameraDevices(
243 [
this](uint32_t cvPixelFormat) {
244 return isCvPixelFormatSupported(cvPixelFormat);
248bool QAVFVideoDevices::isCvPixelFormatSupported(uint32_t cvPixelFormat)
const
250 return !m_isCvPixelFormatSupportedDelegate || m_isCvPixelFormatSupportedDelegate(cvPixelFormat);
255void QAVFVideoDevices::rebuildObserveredAvCaptureDevices()
257 const QList<AVCaptureDevice*> avCaptureDevices = qEnumerateAVCaptureDevices();
259 clearObservedAvCaptureDevices();
261 for (AVCaptureDevice *captureDevice : avCaptureDevices) {
262 ObservedAVCaptureDevice observedDevice;
264 observedDevice.avCaptureDevice = captureDevice;
265 [observedDevice.avCaptureDevice retain];
269 observedDevice.observer = QMacKeyValueObserver(
270 observedDevice.avCaptureDevice,
275 QMetaObject::invokeMethod(
278 rebuildObserveredAvCaptureDevices();
279 QPlatformVideoDevices::onVideoInputsChanged();
283 m_observedAvCaptureDevices.push_back(std::move(observedDevice));
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")