Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
5#include "private/qcameradevice_p.h"
6#include "private/qvideoframe_p.h"
10#include <QtMultimedia/private/qavfcameradebug_p.h>
11#include "avfcamera_p.h"
12#include <avfvideosink_p.h>
13#include <avfvideobuffer_p.h>
14#include "qvideosink.h"
15#include <QtMultimedia/private/qavfhelpers_p.h>
16
17#include <rhi/qrhi.h>
18
19#import <AVFoundation/AVFoundation.h>
20
21#include <QtCore/qmetaobject.h>
22#include <QtMultimedia/qvideoframeformat.h>
23
24QT_USE_NAMESPACE
25
26@interface AVFCaptureFramesDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
27
28- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer;
29
30- (void) captureOutput:(AVCaptureOutput *)captureOutput
31 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
32 fromConnection:(AVCaptureConnection *)connection;
33
34@end
35
36@implementation AVFCaptureFramesDelegate
37{
38@private
39 AVFCameraRenderer *m_renderer;
40}
41
42- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer
43{
44 if (!(self = [super init]))
45 return nil;
46
47 self->m_renderer = renderer;
48 return self;
49}
50
51- (void)captureOutput:(AVCaptureOutput *)captureOutput
52 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
53 fromConnection:(AVCaptureConnection *)connection
54{
55 Q_UNUSED(connection);
56 Q_UNUSED(captureOutput);
57
58 // NB: on iOS captureOutput/connection can be nil (when recording a video -
59 // avfmediaassetwriter).
60
61 auto buffer =
62 std::make_unique<AVFVideoBuffer>(m_renderer,
63 QCFType<CVImageBufferRef>::constructFromGet(
64 CMSampleBufferGetImageBuffer(sampleBuffer)));
65 auto format = buffer->videoFormat();
66 if (!format.isValid()) {
67 return;
68 }
69
70 QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(buffer), format);
71 m_renderer->syncHandleViewfinderFrame(frame);
72}
73
74@end
75
76AVFCameraRenderer::AVFCameraRenderer(QObject *parent)
77 : QObject(parent)
78{
79 m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
80 connect(&m_orientationHandler, &QVideoOutputOrientationHandler::orientationChanged,
81 this, &AVFCameraRenderer::deviceOrientationChanged);
82}
83
85{
86 [m_cameraSession->captureSession() removeOutput:m_videoDataOutput];
87 [m_viewfinderFramesDelegate release];
88 [m_videoDataOutput release];
89
90 if (m_delegateQueue)
91 dispatch_release(m_delegateQueue);
92}
93
95{
96 QMutexLocker lock(&m_vfMutex);
97
98 // ### This is a hack, need to use a reliable way to determine the size and not use the preview layer
99 if (m_layer)
100 m_sink->setNativeSize(QSize(m_layer.bounds.size.width, m_layer.bounds.size.height));
102 deviceOrientationChanged();
103}
104
106{
107 if (!m_videoDataOutput)
108 return;
109
110 if (m_cameraSession) {
111 const auto format = m_cameraSession->cameraFormat();
112 if (format.pixelFormat() != QVideoFrameFormat::Format_Invalid)
113 setPixelFormat(format.pixelFormat(), QCameraFormatPrivate::getColorRange(format));
114 }
115
116 // If no output settings set from above,
117 // it's most likely because the rhi is OpenGL
118 // and the pixel format is not BGRA.
119 // We force this in the base class implementation
120 if (!m_outputSettings)
122
123 if (m_outputSettings)
124 m_videoDataOutput.videoSettings = m_outputSettings;
125}
126
128{
129 m_cameraSession = cameraSession;
130 connect(m_cameraSession, SIGNAL(readyToConfigureConnections()),
131 this, SLOT(updateCaptureConnection()));
132
133 m_needsHorizontalMirroring = false;
134
135 m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
136
137 // Configure video output
138 m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
139 [m_videoDataOutput
140 setSampleBufferDelegate:m_viewfinderFramesDelegate
141 queue:m_delegateQueue];
142
143 [m_cameraSession->captureSession() addOutput:m_videoDataOutput];
144}
145
146void AVFCameraRenderer::updateCaptureConnection()
147{
148 AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
149 if (connection == nil || !m_cameraSession->videoCaptureDevice())
150 return;
151
152 // Frames of front-facing cameras should be mirrored horizontally (it's the default when using
153 // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput)
154 if (connection.isVideoMirroringSupported)
155 connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
156
157 // If the connection does't support mirroring, we'll have to do it ourselves
158 m_needsHorizontalMirroring = !connection.isVideoMirrored
159 && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
160
161 deviceOrientationChanged();
162}
163
164void AVFCameraRenderer::deviceOrientationChanged(int angle)
165{
166 AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
167 if (connection == nil || !m_cameraSession->videoCaptureDevice())
168 return;
169
170 if (!connection.supportsVideoOrientation)
171 return;
172
173 if (angle < 0)
174 angle = m_orientationHandler.currentOrientation();
175
176 AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait;
177 switch (angle) {
178 default:
179 break;
180 case 90:
181 orientation = AVCaptureVideoOrientationLandscapeRight;
182 break;
183 case 180:
184 // this keeps the last orientation, don't do anything
185 return;
186 case 270:
187 orientation = AVCaptureVideoOrientationLandscapeLeft;
188 break;
189 }
190
191 connection.videoOrientation = orientation;
192}
193
194//can be called from non main thread
195void AVFCameraRenderer::syncHandleViewfinderFrame(const QVideoFrame &frame)
196{
197 Q_EMIT newViewfinderFrame(frame);
198
199 QMutexLocker lock(&m_vfMutex);
200
201 if (!m_lastViewfinderFrame.isValid()) {
202 static QMetaMethod handleViewfinderFrameSlot = metaObject()->method(
203 metaObject()->indexOfMethod("handleViewfinderFrame()"));
204
205 handleViewfinderFrameSlot.invoke(this, Qt::QueuedConnection);
206 }
207
208 m_lastViewfinderFrame = frame;
209}
210
212{
213 return m_videoDataOutput;
214}
215
217{
218 return m_viewfinderFramesDelegate;
219}
220
222{
223 [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate queue:m_delegateQueue];
224}
225
226void AVFCameraRenderer::handleViewfinderFrame()
227{
228 QVideoFrame frame;
229 {
230 QMutexLocker lock(&m_vfMutex);
231 frame = m_lastViewfinderFrame;
232 m_lastViewfinderFrame = QVideoFrame();
233 }
234
235 if (m_sink && frame.isValid()) {
236 // frame.setMirroed(m_needsHorizontalMirroring) ?
237 m_sink->setVideoFrame(frame);
238 }
239}
240
241void AVFCameraRenderer::setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat,
242 QVideoFrameFormat::ColorRange colorRange)
243{
244 if (rhi() && rhi()->backend() == QRhi::OpenGLES2) {
245 if (pixelFormat != QVideoFrameFormat::Format_BGRA8888)
246 qWarning() << "OpenGL rhi backend only supports 32BGRA pixel format.";
247 return;
248 }
249
250 // Default to 32BGRA pixel formats on the viewfinder, in case the requested
251 // format can't be used (shouldn't happen unless the developers sets a wrong camera
252 // format on the camera).
253 auto cvPixelFormat = QAVFHelpers::toCVPixelFormat(pixelFormat, colorRange);
254 if (cvPixelFormat == CvPixelFormatInvalid) {
255 cvPixelFormat = kCVPixelFormatType_32BGRA;
256 qWarning() << "QCamera::setCameraFormat: couldn't convert requested pixel format, using ARGB32";
257 }
258
259 bool isSupported = false;
260 NSArray *supportedPixelFormats = m_videoDataOutput.availableVideoCVPixelFormatTypes;
261 for (NSNumber *currentPixelFormat in supportedPixelFormats)
262 {
263 if ([currentPixelFormat unsignedIntValue] == cvPixelFormat) {
264 isSupported = true;
265 break;
266 }
267 }
268
269 if (isSupported) {
270 NSDictionary *outputSettings = @{
271 (NSString *)
272 kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithUnsignedInt:cvPixelFormat]
273#ifndef Q_OS_IOS // On iOS this key generates a warning about 'unsupported key'.
274 ,
275 (NSString *)kCVPixelBufferMetalCompatibilityKey : @true
276#endif // Q_OS_IOS
277 };
278 if (m_outputSettings)
279 [m_outputSettings release];
280 m_outputSettings = [[NSDictionary alloc] initWithDictionary:outputSettings];
281 } else {
282 qWarning() << "QCamera::setCameraFormat: requested pixel format not supported. Did you use a camera format from another camera?";
283 }
284}
285
286#include "moc_avfcamerarenderer_p.cpp"
void reconfigure() override
AVCaptureVideoDataOutput * videoDataOutput() const
void setOutputSettings() override
~AVFCameraRenderer() override
AVFCaptureFramesDelegate * captureDelegate() const
void configureAVCaptureSession(AVFCameraSession *cameraSession)
void setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat, QVideoFrameFormat::ColorRange colorRange)
void resetCaptureDelegate() const
virtual void setOutputSettings()
AVFVideoSink * m_sink