Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
avfcamerarenderer.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "private/qabstractvideobuffer_p.h"
8#include "avfcameradebug_p.h"
9#include "avfcamera_p.h"
10#include <avfvideosink_p.h>
11#include <avfvideobuffer_p.h>
12#include "qvideosink.h"
13#include "qavfhelpers_p.h"
14
15#include <rhi/qrhi.h>
16
17#import <AVFoundation/AVFoundation.h>
18
19#ifdef Q_OS_IOS
20#include <QtGui/qopengl.h>
21#endif
22
23#include <private/qabstractvideobuffer_p.h>
24
25#include <QtMultimedia/qvideoframeformat.h>
26
28
29@interface AVFCaptureFramesDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
30
31- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer;
32
33- (void) captureOutput:(AVCaptureOutput *)captureOutput
34 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
35 fromConnection:(AVCaptureConnection *)connection;
36
37@end
38
39@implementation AVFCaptureFramesDelegate
40{
41@private
42 AVFCameraRenderer *m_renderer;
43}
44
45- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer
46{
47 if (!(self = [super init]))
48 return nil;
49
50 self->m_renderer = renderer;
51 return self;
52}
53
54- (void)captureOutput:(AVCaptureOutput *)captureOutput
55 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
56 fromConnection:(AVCaptureConnection *)connection
57{
59 Q_UNUSED(captureOutput);
60
61 // NB: on iOS captureOutput/connection can be nil (when recording a video -
62 // avfmediaassetwriter).
63
64 CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
65 AVFVideoBuffer *buffer = new AVFVideoBuffer(m_renderer, imageBuffer);
66 auto format = buffer->videoFormat();
67 if (!format.isValid()) {
68 delete buffer;
69 return;
70 }
71
73 m_renderer->syncHandleViewfinderFrame(frame);
74}
75
76@end
77
80{
81 m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
84}
85
87{
88 [m_cameraSession->captureSession() removeOutput:m_videoDataOutput];
90 [m_videoDataOutput release];
91
92 if (m_delegateQueue)
93 dispatch_release(m_delegateQueue);
94#ifdef Q_OS_IOS
95 if (m_textureCache)
96 CFRelease(m_textureCache);
97#endif
98}
99
101{
102 QMutexLocker lock(&m_vfMutex);
103
104 // ### This is a hack, need to use a reliable way to determine the size and not use the preview layer
105 if (m_layer)
106 m_sink->setNativeSize(QSize(m_layer.bounds.size.width, m_layer.bounds.size.height));
109}
110
112{
113 if (!m_videoDataOutput)
114 return;
115
116 const auto cameraPixelFormat = m_cameraSession ? m_cameraSession->cameraFormat().pixelFormat()
118 if (cameraPixelFormat != QVideoFrameFormat::Format_Invalid)
119 setPixelFormat(cameraPixelFormat);
120
121 // If no output settings set from above,
122 // it's most likely because the rhi is OpenGL
123 // and the pixel format is not BGRA.
124 // We force this in the base class implementation
125 if (!m_outputSettings)
127
129 m_videoDataOutput.videoSettings = m_outputSettings;
130}
131
133{
134 m_cameraSession = cameraSession;
135 connect(m_cameraSession, SIGNAL(readyToConfigureConnections()),
136 this, SLOT(updateCaptureConnection()));
137
138 m_needsHorizontalMirroring = false;
139
140 m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
141
142 // Configure video output
143 m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
144 [m_videoDataOutput
145 setSampleBufferDelegate:m_viewfinderFramesDelegate
146 queue:m_delegateQueue];
147
148 [m_cameraSession->captureSession() addOutput:m_videoDataOutput];
149}
150
151void AVFCameraRenderer::updateCaptureConnection()
152{
153 AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
154 if (connection == nil || !m_cameraSession->videoCaptureDevice())
155 return;
156
157 // Frames of front-facing cameras should be mirrored horizontally (it's the default when using
158 // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput)
159 if (connection.isVideoMirroringSupported)
160 connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
161
162 // If the connection does't support mirroring, we'll have to do it ourselves
163 m_needsHorizontalMirroring = !connection.isVideoMirrored
164 && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
165
167}
168
170{
171 AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
172 if (connection == nil || !m_cameraSession->videoCaptureDevice())
173 return;
174
175 if (!connection.supportsVideoOrientation)
176 return;
177
178 if (angle < 0)
179 angle = m_orientationHandler.currentOrientation();
180
181 AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait;
182 switch (angle) {
183 default:
184 break;
185 case 90:
186 orientation = AVCaptureVideoOrientationLandscapeRight;
187 break;
188 case 180:
189 // this keeps the last orientation, don't do anything
190 return;
191 case 270:
192 orientation = AVCaptureVideoOrientationLandscapeLeft;
193 break;
194 }
195
196 connection.videoOrientation = orientation;
197}
198
199//can be called from non main thread
201{
203
204 QMutexLocker lock(&m_vfMutex);
205
206 if (!m_lastViewfinderFrame.isValid()) {
207 static QMetaMethod handleViewfinderFrameSlot = metaObject()->method(
208 metaObject()->indexOfMethod("handleViewfinderFrame()"));
209
210 handleViewfinderFrameSlot.invoke(this, Qt::QueuedConnection);
211 }
212
213 m_lastViewfinderFrame = frame;
214}
215
216AVCaptureVideoDataOutput *AVFCameraRenderer::videoDataOutput() const
217{
218 return m_videoDataOutput;
219}
220
222{
223 return m_viewfinderFramesDelegate;
224}
225
227{
228 [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate queue:m_delegateQueue];
229}
230
231void AVFCameraRenderer::handleViewfinderFrame()
232{
234 {
235 QMutexLocker lock(&m_vfMutex);
236 frame = m_lastViewfinderFrame;
237 m_lastViewfinderFrame = QVideoFrame();
238 }
239
240 if (m_sink && frame.isValid()) {
241 // ### pass format to surface
242 QVideoFrameFormat format = frame.surfaceFormat();
243 if (m_needsHorizontalMirroring)
244 format.setMirrored(true);
245
247 }
248}
249
251{
252 if (rhi() && rhi()->backend() == QRhi::OpenGLES2) {
253 if (pixelFormat != QVideoFrameFormat::Format_BGRA8888)
254 qWarning() << "OpenGL rhi backend only supports 32BGRA pixel format.";
255 return;
256 }
257
258 // Default to 32BGRA pixel formats on the viewfinder, in case the requested
259 // format can't be used (shouldn't happen unless the developers sets a wrong camera
260 // format on the camera).
261 unsigned avPixelFormat = kCVPixelFormatType_32BGRA;
262 if (!QAVFHelpers::toCVPixelFormat(pixelFormat, avPixelFormat))
263 qWarning() << "QCamera::setCameraFormat: couldn't convert requested pixel format, using ARGB32";
264
265 bool isSupported = false;
266 NSArray *supportedPixelFormats = m_videoDataOutput.availableVideoCVPixelFormatTypes;
267 for (NSNumber *currentPixelFormat in supportedPixelFormats)
268 {
269 if ([currentPixelFormat unsignedIntValue] == avPixelFormat) {
270 isSupported = true;
271 break;
272 }
273 }
274
275 if (isSupported) {
276 NSDictionary* outputSettings = @{
277 (NSString *)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:avPixelFormat]
278#ifndef Q_OS_IOS // On iOS this key generates a warning about 'unsupported key'.
279 , (NSString *)kCVPixelBufferMetalCompatibilityKey: @true
280#endif // Q_OS_IOS
281 };
283 [m_outputSettings release];
284 m_outputSettings = [[NSDictionary alloc] initWithDictionary:outputSettings];
285 } else {
286 qWarning() << "QCamera::setCameraFormat: requested pixel format not supported. Did you use a camera format from another camera?";
287 }
288}
289
290#include "moc_avfcamerarenderer_p.cpp"
291
void reconfigure() override
void syncHandleViewfinderFrame(const QVideoFrame &frame)
AVFCameraRenderer(QObject *parent=nullptr)
void newViewfinderFrame(const QVideoFrame &frame)
AVCaptureVideoDataOutput * videoDataOutput() const
void setOutputSettings() override
void setPixelFormat(const QVideoFrameFormat::PixelFormat format)
AVFCaptureFramesDelegate * captureDelegate() const
void configureAVCaptureSession(AVFCameraSession *cameraSession)
void deviceOrientationChanged(int angle=-1)
void resetCaptureDelegate() const
AVCaptureDevice * videoCaptureDevice() const
QCameraFormat cameraFormat() const
NSDictionary * m_outputSettings
virtual void setOutputSettings()
AVFVideoSink * m_sink
void setNativeSize(QSize size)
QVideoFrameFormat::PixelFormat pixelFormat
\qmlproperty enumeration QtMultimedia::cameraFormat::pixelFormat
\inmodule QtCore
Definition qmetaobject.h:18
bool invoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument()) const
\obsolete [6.5] Please use the variadic overload of this function
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
virtual void setVideoFrame(const QVideoFrame &frame)
@ OpenGLES2
Definition qrhi.h:1772
\inmodule QtCore
Definition qsize.h:25
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
PixelFormat
Enumerates video data types.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:26
bool isValid() const
Identifies whether a video frame is valid.
void orientationChanged(int angle)
bool toCVPixelFormat(QVideoFrameFormat::PixelFormat qtFormat, unsigned &conv)
@ QueuedConnection
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection * connection
#define qWarning
Definition qlogging.h:162
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLenum GLuint buffer
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 Q_UNUSED(x)
obj metaObject() -> className()
QReadWriteLock lock
[0]
QFrame frame
[0]
QSvgRenderer * renderer
[0]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent