60int AVFImageCapture::doCapture(
const QString &fileName)
63 QMetaObject::invokeMethod(
this, [
this, id = m_lastCaptureId] {
64 error(id, QImageCapture::ResourceError, QPlatformImageCapture::msgImageCaptureNotSet());
65 }, Qt::QueuedConnection);
68 if (!isReadyForCapture()) {
69 QMetaObject::invokeMethod(
this, [
this, id = m_lastCaptureId] {
70 error(id, QImageCapture::NotReadyError, QPlatformImageCapture::msgCameraNotReady());
71 }, Qt::QueuedConnection);
76 bool captureToBuffer = fileName.isEmpty();
78 auto request = CaptureRequest{
80 std::make_unique<QSemaphore>(),
82 m_requestsMutex.lock();
83 m_captureRequests.push(request);
84 m_requestsMutex.unlock();
87 captureStillImageAsynchronouslyFromConnection:m_videoConnection
88 completionHandler:[=,
this](CMSampleBufferRef imageSampleBuffer,
91 QStringList messageParts{
92 QString::fromNSString(nsError.localizedDescription),
93 QString::fromNSString(nsError.localizedFailureReason),
94 QString::fromNSString(nsError.localizedRecoverySuggestion),
97 QString errorMessage = messageParts.join(QChar(u' '));
98 qCDebug(qLcCamera) <<
"Image capture failed:" << errorMessage;
100 QMetaObject::invokeMethod(
this, [
this, captureId = request.captureId, errorMessage] {
101 error(captureId, QImageCapture::ResourceError, errorMessage);
102 }, Qt::QueuedConnection);
115 if (!m_session->videoOutput() || request.previewReady->tryAcquire(1, 1000)) {
116 qCDebug(qLcCamera) <<
"Image capture completed";
118 NSData *nsJpgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
119 QByteArray jpgData = QByteArray::fromRawData((
const char *)[nsJpgData bytes], [nsJpgData length]);
121 if (captureToBuffer) {
122 QBuffer data(&jpgData);
123 QImageReader reader(&data,
"JPEG");
124 QSize size = reader.size();
125 auto buffer = std::make_unique<QMemoryVideoBuffer>(
126 QByteArray(jpgData.constData(), jpgData.size()), -1);
127 QVideoFrame frame = QVideoFramePrivate::createFrame(
128 std::move(buffer), QVideoFrameFormat(size, QVideoFrameFormat::Format_Jpeg));
129 QMetaObject::invokeMethod(
this, [
this, captureId = request.captureId, frame] {
130 imageAvailable(captureId, frame);
131 }, Qt::QueuedConnection);
134 if (f.open(QFile::WriteOnly)) {
135 if (f.write(jpgData) != -1) {
136 QMetaObject::invokeMethod(
this,
137 [
this, captureId = request.captureId, fileName] {
138 imageSaved(captureId, fileName);
139 }, Qt::QueuedConnection);
141 QString errorString = f.errorString();
142 QMetaObject::invokeMethod(
143 this, [
this, captureId = request.captureId, errorString] {
144 error(captureId, QImageCapture::OutOfSpaceError, errorString);
145 }, Qt::QueuedConnection);
148 QString errorMessage = tr(
"Could not open destination file:\n%1").arg(fileName);
149 QMetaObject::invokeMethod(
this,
150 [
this, captureId = request.captureId, errorMessage] {
151 error(captureId, QImageCapture::ResourceError, errorMessage);
152 }, Qt::QueuedConnection);
156 const QString errorMessage = u"Image capture failed: timed out waiting"
157 " for a preview frame."_s;
158 qCDebug(qLcCamera) << errorMessage;
159 QMetaObject::invokeMethod(
this, [
this, captureId = request.captureId, errorMessage] {
160 error(captureId, QImageCapture::ResourceError, errorMessage);
161 }, Qt::QueuedConnection);
165 return request.captureId;
242QImageEncoderSettings AVFImageCapture::imageSettings()
const
244 QImageEncoderSettings settings;
246 if (!videoCaptureDeviceIsValid())
249 AVCaptureDevice *captureDevice = m_service
->session()->videoCaptureDevice();
250 if (!captureDevice.activeFormat) {
251 qCDebug(qLcCamera) << Q_FUNC_INFO <<
"no active format";
255 QSize res(qt_device_format_resolution(captureDevice.activeFormat));
257 if (!m_service->avfImageCaptureControl() || !m_service->avfImageCaptureControl()->stillImageOutput()) {
258 qCDebug(qLcCamera) << Q_FUNC_INFO <<
"no still image output";
262 AVCaptureStillImageOutput *stillImageOutput = m_service->avfImageCaptureControl()->stillImageOutput();
263 if (stillImageOutput.highResolutionStillImageOutputEnabled)
264 res = qt_device_format_high_resolution(captureDevice.activeFormat);
266 if (res.isNull() || !res.isValid()) {
267 qCDebug(qLcCamera) << Q_FUNC_INFO <<
"failed to exctract the image resolution";
271 settings.setResolution(res);
272 settings.setFormat(QImageCapture::JPEG);
286bool AVFImageCapture::applySettings()
288 if (!videoCaptureDeviceIsValid())
295 if (!m_service->imageCapture()
297 qCDebug(qLcCamera) << Q_FUNC_INFO <<
"no still image output";
301 if (m_settings.format() != QImageCapture::UnspecifiedFormat && m_settings.format() != QImageCapture::JPEG) {
302 qCDebug(qLcCamera) << Q_FUNC_INFO <<
"unsupported format:" << m_settings.format();
306 QSize res(m_settings.resolution());
308 qCDebug(qLcCamera) << Q_FUNC_INFO <<
"invalid resolution:" << res;
312 if (!res.isValid()) {
320 bool activeFormatChanged =
false;
322 AVCaptureDevice *captureDevice = m_service
->session()->videoCaptureDevice();
323 AVCaptureDeviceFormat *match = qt_find_best_resolution_match(captureDevice, res,
327 qCDebug(qLcCamera) << Q_FUNC_INFO <<
"unsupported resolution:" << res;
331 activeFormatChanged = qt_set_active_format(captureDevice, match,
true);
334 AVCaptureStillImageOutput *imageOutput = m_service->avfImageCaptureControl()->stillImageOutput();
335 if (res == qt_device_format_high_resolution(captureDevice.activeFormat))
336 imageOutput.highResolutionStillImageOutputEnabled = YES;
338 imageOutput.highResolutionStillImageOutputEnabled = NO;
341 return activeFormatChanged;
344void AVFImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
347 if (m_service == captureSession)
350 m_service = captureSession;
352 m_session->disconnect(
this);
354 m_cameraControl->disconnect(
this);
356 m_cameraControl =
nullptr;
357 m_videoConnection = nil;
362 connect(m_service, &AVFCameraService::cameraChanged,
this, &AVFImageCapture::onCameraChanged);
363 connect(m_session, &AVFCameraSession::readyToConfigureConnections,
this, &AVFImageCapture::updateCaptureConnection);
364 connect(m_session, &AVFCameraSession::newViewfinderFrame,
365 this, &AVFImageCapture::onNewViewfinderFrame);
368 updateCaptureConnection();