1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#include <qimagecapture.h>
4#include <private/qplatformimagecapture_p.h>
5#include <qmediametadata.h>
6#include <private/qplatformmediacapture_p.h>
7#include <private/qplatformmediaintegration_p.h>
8#include <private/qplatformmediaformatinfo_p.h>
9#include <qmediacapturesession.h>
10
11#include "private/qobject_p.h"
12#include <qcamera.h>
13#include <private/qplatformcamera_p.h>
14#include <QtCore/qdebug.h>
15#include <QtCore/qurl.h>
16#include <QtCore/qstringlist.h>
17#include <QtCore/qmetaobject.h>
18
19QT_BEGIN_NAMESPACE
20
21/*!
22 \class QImageCapture
23 \inmodule QtMultimedia
24 \ingroup multimedia
25 \ingroup multimedia_camera
26
27
28 \brief The QImageCapture class is used for the recording of media content.
29
30 The QImageCapture class is a high level images recording class.
31 It's not intended to be used alone but for accessing the media
32 recording functions of other media objects, like QCamera.
33
34 \snippet multimedia-snippets/camera.cpp Camera
35
36 \snippet multimedia-snippets/camera.cpp Camera keys
37
38 \sa QCamera
39*/
40
41class QImageCapturePrivate
42{
43 Q_DECLARE_PUBLIC(QImageCapture)
44public:
45 QCamera *camera = nullptr;
46
47 QMediaCaptureSession *captureSession = nullptr;
48 QPlatformImageCapture *control = nullptr;
49
50 QImageCapture::Error error = QImageCapture::NoError;
51 QString errorString;
52 QMediaMetaData metaData;
53
54 void _q_error(int id, int error, const QString &errorString);
55
56 void unsetError() { error = QImageCapture::NoError; errorString.clear(); }
57
58 QImageCapture *q_ptr;
59};
60
61void QImageCapturePrivate::_q_error(int id, int error, const QString &errorString)
62{
63 Q_Q(QImageCapture);
64
65 this->error = QImageCapture::Error(error);
66 this->errorString = errorString;
67
68 emit q->errorChanged();
69 emit q->errorOccurred(id, error: this->error, errorString);
70}
71
72/*!
73 Constructs a image capture object, from a \a parent, that can capture
74 individual still images produced by a camera.
75
76 You must connect both an image capture object and a QCamera to a capture
77 session to capture images.
78*/
79
80QImageCapture::QImageCapture(QObject *parent)
81 : QObject(parent), d_ptr(new QImageCapturePrivate)
82{
83 Q_D(QImageCapture);
84 d->q_ptr = this;
85
86 auto maybeControl = QPlatformMediaIntegration::instance()->createImageCapture(this);
87 if (!maybeControl) {
88 qWarning() << "Failed to initialize QImageCapture" << maybeControl.error();
89 d->errorString = maybeControl.error();
90 d->error = NotReadyError;
91 return;
92 }
93
94 d->control = maybeControl.value();
95 connect(sender: d->control, signal: &QPlatformImageCapture::imageExposed, context: this, slot: &QImageCapture::imageExposed);
96 connect(sender: d->control, signal: &QPlatformImageCapture::imageCaptured, context: this, slot: &QImageCapture::imageCaptured);
97 connect(sender: d->control, signal: &QPlatformImageCapture::imageMetadataAvailable, context: this,
98 slot: &QImageCapture::imageMetadataAvailable);
99 connect(sender: d->control, signal: &QPlatformImageCapture::imageAvailable, context: this,
100 slot: &QImageCapture::imageAvailable);
101 connect(sender: d->control, signal: &QPlatformImageCapture::imageSaved, context: this, slot: &QImageCapture::imageSaved);
102 connect(sender: d->control, signal: &QPlatformImageCapture::readyForCaptureChanged, context: this,
103 slot: &QImageCapture::readyForCaptureChanged);
104 connect(sender: d->control, SIGNAL(error(int,int,QString)),
105 receiver: this, SLOT(_q_error(int,int,QString)));
106}
107
108/*!
109 \fn void QImageCapture::imageMetadataAvailable(int id, const QMediaMetaData &metaData)
110
111 Signals that an image identified by \a id has \a metaData.
112*/
113
114/*!
115 \internal
116*/
117void QImageCapture::setCaptureSession(QMediaCaptureSession *session)
118{
119 Q_D(QImageCapture);
120 d->captureSession = session;
121}
122
123/*!
124 Destroys images capture object.
125*/
126
127QImageCapture::~QImageCapture()
128{
129 if (d_ptr->captureSession)
130 d_ptr->captureSession->setImageCapture(nullptr);
131 delete d_ptr;
132}
133
134/*!
135 Returns true if the images capture service ready to use.
136*/
137bool QImageCapture::isAvailable() const
138{
139 return d_func()->control && d_func()->captureSession && d_func()->captureSession->camera();
140}
141
142/*!
143 Returns the capture session this camera is connected to, or
144 a nullptr if the camera is not connected to a capture session.
145
146 Use QMediaCaptureSession::setImageCapture() to connect the image capture to
147 a session.
148*/
149QMediaCaptureSession *QImageCapture::captureSession() const
150{
151 return d_ptr->captureSession;
152}
153
154/*!
155 \qmlproperty enumeration QtMultimedia::ImageCapture::error
156
157 This property holds the last error type that occurred. It can be one of the following.
158
159 \value ImageCapture.NoError No esrrors.
160 \value ImageCapture.NotReadyError The service is not ready for capture yet.
161 \value ImageCapture.ResourceError Device is not ready or not available.
162 \value ImageCapture.OutOfSpaceError No space left on device.
163 \value ImageCapture.NotSupportedFeatureError Device does not support stillimages capture.
164 \value ImageCapture.FormatError Current format is not supported.
165
166 \sa errorString
167*/
168
169/*!
170 \property QImageCapture::error
171
172 Returns the current error state.
173
174 \sa errorString()
175*/
176
177QImageCapture::Error QImageCapture::error() const
178{
179 return d_func()->error;
180}
181
182/*!
183 \qmlproperty string QtMultimedia::ImageCapture::errorString
184
185 This property holds the last error message that occurred.
186
187 \sa error
188*/
189
190/*!
191 \property QImageCapture::errorString
192
193 Returns a string describing the current error state.
194
195 \sa error()
196*/
197
198QString QImageCapture::errorString() const
199{
200 return d_func()->errorString;
201}
202
203/*!
204 \qmlproperty mediaMetaData QtMultimedia::ImageCapture::metaData
205
206 This property holds the metadata that wil be embedded into the image.
207*/
208
209/*!
210 \property QImageCapture::metaData
211 \brief The meta data that will get embedded into the image.
212
213 \note Additional fields such as a time stamp or location may get added by
214 the camera back end.
215*/
216QMediaMetaData QImageCapture::metaData() const
217{
218 Q_D(const QImageCapture);
219 return d->metaData;
220}
221
222/*!
223 Replaces any existing meta data, to be embedded into the captured image,
224 with a set of \a metaData.
225*/
226void QImageCapture::setMetaData(const QMediaMetaData &metaData)
227{
228 Q_D(QImageCapture);
229 d->metaData = metaData;
230 if (d->control)
231 d->control->setMetaData(d->metaData);
232 emit metaDataChanged();
233}
234
235/*!
236 Adds additional \a metaData to any existing meta data, that is embedded
237 into the captured image.
238*/
239void QImageCapture::addMetaData(const QMediaMetaData &metaData)
240{
241 Q_D(QImageCapture);
242 auto data = d->metaData;
243 for (auto &&[key, value] : metaData.asKeyValueRange())
244 data.insert(k: key, value);
245 setMetaData(data);
246}
247
248/*!
249 \property QImageCapture::readyForCapture
250
251 Holds \c true if the camera is ready to capture an image immediately.
252 Calling capture() while \c readyForCapture is \c false is not
253 permitted and results in an error.
254*/
255bool QImageCapture::isReadyForCapture() const
256{
257 Q_D(const QImageCapture);
258 if (!d->control || !d->captureSession || !d->control->isReadyForCapture())
259 return false;
260 auto *camera = d->captureSession->camera();
261 if (!camera || !camera->isActive())
262 return false;
263 return true;
264}
265
266/*!
267 \fn QImageCapture::readyForCaptureChanged(bool ready)
268
269 Signals that a camera's \a ready for capture state has changed.
270*/
271
272
273/*!
274 Capture the image and save it to \a file.
275 This operation is asynchronous in majority of cases,
276 followed by signals QImageCapture::imageExposed(),
277 QImageCapture::imageCaptured(), QImageCapture::imageSaved()
278 or QImageCapture::error().
279
280 If an empty \a file is passed, the camera back end chooses
281 the default location and naming scheme for photos on the system,
282 if only file name without full path is specified, the image will be saved to
283 the default directory, with a full path reported with imageCaptured() and imageSaved() signals.
284
285 QCamera saves all the capture parameters like exposure settings or
286 image processing parameters, so changes to camera parameters after
287 capture() is called do not affect previous capture requests.
288
289 QImageCapture::capture returns the capture Id parameter, used with
290 imageExposed(), imageCaptured() and imageSaved() signals.
291
292 \sa isReadyForCapture()
293*/
294int QImageCapture::captureToFile(const QString &file)
295{
296 Q_D(QImageCapture);
297 if (!d->control) {
298 d->_q_error(id: -1, error: d->error, errorString: d->errorString);
299 return -1;
300 }
301
302 d->unsetError();
303
304 if (!isReadyForCapture()) {
305 d->_q_error(id: -1, error: NotReadyError, errorString: tr(s: "Could not capture in stopped state"));
306 return -1;
307 }
308
309 return d->control->capture(fileName: file);
310}
311
312/*!
313 Capture the image and make it available as a QImage.
314 This operation is asynchronous in majority of cases,
315 followed by signals QImageCapture::imageExposed(),
316 QImageCapture::imageCaptured()
317 or QImageCapture::error().
318
319 QImageCapture::capture returns the capture Id parameter, used with
320 imageExposed(), imageCaptured() and imageSaved() signals.
321
322 \sa isReadyForCapture()
323*/
324int QImageCapture::capture()
325{
326 Q_D(QImageCapture);
327 if (!d->control) {
328 d->_q_error(id: -1, error: d->error, errorString: d->errorString);
329 return -1;
330 } else {
331 d->unsetError();
332 return d->control->captureToBuffer();
333 }
334}
335
336/*!
337 \enum QImageCapture::Error
338
339 \value NoError No Errors.
340 \value NotReadyError The service is not ready for capture yet.
341 \value ResourceError Device is not ready or not available.
342 \value OutOfSpaceError No space left on device.
343 \value NotSupportedFeatureError Device does not support stillimages capture.
344 \value FormatError Current format is not supported.
345*/
346
347/*!
348 \fn QImageCapture::errorOccurred(int id, QImageCapture::Error error, const QString &errorString);
349
350 Signals that the capture request \a id has failed with an \a error
351 and \a errorString description.
352*/
353
354/*!
355 \fn QImageCapture::imageExposed(int id)
356
357 Signal emitted when the frame with request \a id was exposed.
358*/
359
360/*!
361 \fn QImageCapture::imageCaptured(int id, const QImage &preview);
362
363 Signal emitted when the frame with request \a id was captured, but not
364 processed and saved yet. Frame \a preview can be displayed to user.
365*/
366
367/*!
368 \fn QImageCapture::imageAvailable(int id, const QVideoFrame &frame)
369
370 Signal emitted when the \a frame with request \a id is available.
371*/
372
373/*!
374 \fn QImageCapture::imageSaved(int id, const QString &fileName)
375
376 Signal emitted when QImageCapture::CaptureToFile is set and
377 the frame with request \a id was saved to \a fileName.
378*/
379
380/*!
381 \enum QImageCapture::FileFormat
382
383 Choose one of the following image formats:
384
385 \value UnspecifiedFormat No format specified
386 \value JPEG \c .jpg or \c .jpeg format
387 \value PNG \c .png format
388 \value WebP \c .webp format
389 \value Tiff \c .tiff format
390 \omitvalue LastFileFormat
391*/
392
393/*!
394 \qmlproperty enumeration QtMultimedia::ImageCapture::fileFormat
395
396 This property holds the file format for which the image will be
397 written. It can be one of the following.
398
399 \value UnspecifiedFormat No format specified
400 \value JPEG \c .jpg or \c .jpeg format
401 \value PNG \c .png format
402 \value WebP \c .webp format
403 \value Tiff \c .tiff format
404*/
405
406/*!
407 \property QImageCapture::fileFormat
408 \brief The image format.
409*/
410
411QImageCapture::FileFormat QImageCapture::fileFormat() const
412{
413 Q_D(const QImageCapture);
414 return d->control ? d->control->imageSettings().format() : UnspecifiedFormat;
415}
416
417/*!
418 Sets the image \a format.
419
420 Assigning an unsupported \l FileFormat has no effect.
421
422 \sa supportedFormats
423*/
424void QImageCapture::setFileFormat(QImageCapture::FileFormat format)
425{
426 Q_D(QImageCapture);
427 if (!d->control)
428 return;
429 auto fmt = d->control->imageSettings();
430 const FileFormat oldFormat = fmt.format();
431 if (oldFormat == format)
432 return;
433 fmt.setFormat(format);
434 d->control->setImageSettings(fmt);
435 // Only fire the signal if the format was applied.
436 if (oldFormat != fileFormat())
437 emit fileFormatChanged();
438}
439
440/*!
441 \qmlproperty list<FileFormat> QtMultimedia::ImageCapture::supportedFormats
442 \since 6.10
443
444 Contains a list of supported file formats.
445
446 \sa fileFormat
447*/
448
449/*!
450 Returns a list of supported file formats.
451
452 \sa {QImageCapture::}{FileFormat}
453*/
454QList<QImageCapture::FileFormat> QImageCapture::supportedFormats()
455{
456 return QPlatformMediaIntegration::instance()->formatInfo()->imageFormats;
457}
458
459/*!
460 Returns the name of the given format, \a f.
461*/
462QString QImageCapture::fileFormatName(QImageCapture::FileFormat f)
463{
464 const char *name = nullptr;
465 switch (f) {
466 case UnspecifiedFormat:
467 name = "Unspecified image format";
468 break;
469 case JPEG:
470 name = "JPEG";
471 break;
472 case PNG:
473 name = "PNG";
474 break;
475 case WebP:
476 name = "WebP";
477 break;
478 case Tiff:
479 name = "Tiff";
480 break;
481 }
482 return QString::fromUtf8(utf8: name);
483}
484
485/*!
486 Returns the description of the given file format, \a f.
487*/
488QString QImageCapture::fileFormatDescription(QImageCapture::FileFormat f)
489{
490 const char *name = nullptr;
491 switch (f) {
492 case UnspecifiedFormat:
493 name = "Unspecified image format";
494 break;
495 case JPEG:
496 name = "JPEG";
497 break;
498 case PNG:
499 name = "PNG";
500 break;
501 case WebP:
502 name = "WebP";
503 break;
504 case Tiff:
505 name = "Tiff";
506 break;
507 }
508 return QString::fromUtf8(utf8: name);
509}
510
511/*!
512 Returns the resolution of the encoded image.
513*/
514
515QSize QImageCapture::resolution() const
516{
517 Q_D(const QImageCapture);
518 return d->control ? d->control->imageSettings().resolution() : QSize{};
519}
520
521/*!
522 \fn void QImageCapture::resolutionChanged()
523
524 Signals when the image resolution changes.
525*/
526
527/*!
528 Sets the \a resolution of the encoded image.
529
530 An empty QSize indicates the encoder should make an optimal choice based on
531 what is available from the image source and the limitations of the codec.
532*/
533void QImageCapture::setResolution(const QSize &resolution)
534{
535 Q_D(QImageCapture);
536 if (!d->control)
537 return;
538 auto fmt = d->control->imageSettings();
539 if (fmt.resolution() == resolution)
540 return;
541 fmt.setResolution(resolution);
542 d->control->setImageSettings(fmt);
543 emit resolutionChanged();
544}
545
546/*!
547 Sets the \a width and \a height of the resolution of the encoded image.
548
549 \overload
550*/
551void QImageCapture::setResolution(int width, int height)
552{
553 setResolution(QSize(width, height));
554}
555
556/*!
557 \enum QImageCapture::Quality
558
559 Enumerates quality encoding levels.
560
561 \value VeryLowQuality
562 \value LowQuality
563 \value NormalQuality
564 \value HighQuality
565 \value VeryHighQuality
566*/
567
568/*!
569 \qmlproperty enumeration QtMultimedia::ImageCapture::quality
570
571 This property holds the quality hint when storing the captured
572 image. It can be one of the following values.
573
574 \value VeryLowQuality Very low
575 \value LowQuality Low
576 \value NormalQuality Normal
577 \value HighQuality High
578 \value VeryHighQuality Very high
579*/
580
581/*!
582 \property QImageCapture::quality
583 \brief The image encoding quality.
584*/
585QImageCapture::Quality QImageCapture::quality() const
586{
587 Q_D(const QImageCapture);
588 return d->control ? d->control->imageSettings().quality() : NormalQuality;
589}
590
591/*!
592 Sets the image encoding \a quality.
593*/
594void QImageCapture::setQuality(Quality quality)
595{
596 Q_D(QImageCapture);
597 if (!d->control)
598 return;
599 auto fmt = d->control->imageSettings();
600 if (fmt.quality() == quality)
601 return;
602 fmt.setQuality(quality);
603 d->control->setImageSettings(fmt);
604 emit qualityChanged();
605}
606
607/*!
608 \internal
609*/
610QPlatformImageCapture *QImageCapture::platformImageCapture()
611{
612 Q_D(QImageCapture);
613 return d->control;
614}
615
616QT_END_NAMESPACE
617
618#include "moc_qimagecapture.cpp"
619

source code of qtmultimedia/src/multimedia/camera/qimagecapture.cpp