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(imageExposed(int)),
96 receiver: this, SIGNAL(imageExposed(int)));
97 connect(sender: d->control, SIGNAL(imageCaptured(int,QImage)),
98 receiver: this, SIGNAL(imageCaptured(int,QImage)));
99 connect(sender: d->control, SIGNAL(imageMetadataAvailable(int,QMediaMetaData)),
100 receiver: this, SIGNAL(imageMetadataAvailable(int,QMediaMetaData)));
101 connect(sender: d->control, SIGNAL(imageAvailable(int,QVideoFrame)),
102 receiver: this, SIGNAL(imageAvailable(int,QVideoFrame)));
103 connect(sender: d->control, SIGNAL(imageSaved(int,QString)),
104 receiver: this, SIGNAL(imageSaved(int,QString)));
105 connect(sender: d->control, SIGNAL(readyForCaptureChanged(bool)),
106 receiver: this, SIGNAL(readyForCaptureChanged(bool)));
107 connect(sender: d->control, SIGNAL(error(int,int,QString)),
108 receiver: this, SLOT(_q_error(int,int,QString)));
109}
110
111/*!
112 \fn void QImageCapture::imageMetadataAvailable(int id, const QMediaMetaData &metaData)
113
114 Signals that an image identified by \a id has \a metaData.
115*/
116
117/*!
118 \internal
119*/
120void QImageCapture::setCaptureSession(QMediaCaptureSession *session)
121{
122 Q_D(QImageCapture);
123 d->captureSession = session;
124}
125
126/*!
127 Destroys images capture object.
128*/
129
130QImageCapture::~QImageCapture()
131{
132 if (d_ptr->captureSession)
133 d_ptr->captureSession->setImageCapture(nullptr);
134 delete d_ptr;
135}
136
137/*!
138 Returns true if the images capture service ready to use.
139*/
140bool QImageCapture::isAvailable() const
141{
142 return d_func()->control && d_func()->captureSession && d_func()->captureSession->camera();
143}
144
145/*!
146 Returns the capture session this camera is connected to, or
147 a nullptr if the camera is not connected to a capture session.
148
149 Use QMediaCaptureSession::setImageCapture() to connect the image capture to
150 a session.
151*/
152QMediaCaptureSession *QImageCapture::captureSession() const
153{
154 return d_ptr->captureSession;
155}
156
157/*!
158 \property QImageCapture::error
159
160 Returns the current error state.
161
162 \sa errorString()
163*/
164
165QImageCapture::Error QImageCapture::error() const
166{
167 return d_func()->error;
168}
169
170/*!
171 \property QImageCapture::errorString
172
173 Returns a string describing the current error state.
174
175 \sa error()
176*/
177
178QString QImageCapture::errorString() const
179{
180 return d_func()->errorString;
181}
182
183/*!
184 \property QImageCapture::metaData
185 \brief The meta data that will get embedded into the image.
186
187 \note Additional fields such as a time stamp or location may get added by
188 the camera back end.
189*/
190QMediaMetaData QImageCapture::metaData() const
191{
192 Q_D(const QImageCapture);
193 return d->metaData;
194}
195
196/*!
197 Replaces any existing meta data, to be embedded into the captured image,
198 with a set of \a metaData.
199*/
200void QImageCapture::setMetaData(const QMediaMetaData &metaData)
201{
202 Q_D(QImageCapture);
203 d->metaData = metaData;
204 if (d->control)
205 d->control->setMetaData(d->metaData);
206 emit metaDataChanged();
207}
208
209/*!
210 Adds additional \a metaData to any existing meta data, that is embedded
211 into the captured image.
212*/
213void QImageCapture::addMetaData(const QMediaMetaData &metaData)
214{
215 Q_D(QImageCapture);
216 auto data = d->metaData;
217 for (auto k : metaData.keys())
218 data.insert(k, value: metaData.value(k));
219 setMetaData(data);
220}
221
222/*!
223 \property QImageCapture::readyForCapture
224
225 Holds \c true if the camera is ready to capture an image immediately.
226 Calling capture() while \c readyForCapture is \c false is not
227 permitted and results in an error.
228*/
229bool QImageCapture::isReadyForCapture() const
230{
231 Q_D(const QImageCapture);
232 if (!d->control || !d->captureSession || !d->control->isReadyForCapture())
233 return false;
234 auto *camera = d->captureSession->camera();
235 if (!camera || !camera->isActive())
236 return false;
237 return true;
238}
239
240/*!
241 \fn QImageCapture::readyForCaptureChanged(bool ready)
242
243 Signals that a camera's \a ready for capture state has changed.
244*/
245
246
247/*!
248 Capture the image and save it to \a file.
249 This operation is asynchronous in majority of cases,
250 followed by signals QImageCapture::imageExposed(),
251 QImageCapture::imageCaptured(), QImageCapture::imageSaved()
252 or QImageCapture::error().
253
254 If an empty \a file is passed, the camera back end chooses
255 the default location and naming scheme for photos on the system,
256 if only file name without full path is specified, the image will be saved to
257 the default directory, with a full path reported with imageCaptured() and imageSaved() signals.
258
259 QCamera saves all the capture parameters like exposure settings or
260 image processing parameters, so changes to camera parameters after
261 capture() is called do not affect previous capture requests.
262
263 QImageCapture::capture returns the capture Id parameter, used with
264 imageExposed(), imageCaptured() and imageSaved() signals.
265
266 \sa isReadyForCapture()
267*/
268int QImageCapture::captureToFile(const QString &file)
269{
270 Q_D(QImageCapture);
271 if (!d->control) {
272 d->_q_error(id: -1, error: d->error, errorString: d->errorString);
273 return -1;
274 }
275
276 d->unsetError();
277
278 if (!isReadyForCapture()) {
279 d->_q_error(id: -1, error: NotReadyError, errorString: tr(s: "Could not capture in stopped state"));
280 return -1;
281 }
282
283 return d->control->capture(fileName: file);
284}
285
286/*!
287 Capture the image and make it available as a QImage.
288 This operation is asynchronous in majority of cases,
289 followed by signals QImageCapture::imageExposed(),
290 QImageCapture::imageCaptured()
291 or QImageCapture::error().
292
293 QImageCapture::capture returns the capture Id parameter, used with
294 imageExposed(), imageCaptured() and imageSaved() signals.
295
296 \sa isReadyForCapture()
297*/
298int QImageCapture::capture()
299{
300 Q_D(QImageCapture);
301 if (!d->control) {
302 d->_q_error(id: -1, error: d->error, errorString: d->errorString);
303 return -1;
304 } else {
305 d->unsetError();
306 return d->control->captureToBuffer();
307 }
308}
309
310/*!
311 \enum QImageCapture::Error
312
313 \value NoError No Errors.
314 \value NotReadyError The service is not ready for capture yet.
315 \value ResourceError Device is not ready or not available.
316 \value OutOfSpaceError No space left on device.
317 \value NotSupportedFeatureError Device does not support stillimages capture.
318 \value FormatError Current format is not supported.
319*/
320
321/*!
322 \fn QImageCapture::errorOccurred(int id, QImageCapture::Error error, const QString &errorString);
323
324 Signals that the capture request \a id has failed with an \a error
325 and \a errorString description.
326*/
327
328/*!
329 \fn QImageCapture::imageExposed(int id)
330
331 Signal emitted when the frame with request \a id was exposed.
332*/
333
334/*!
335 \fn QImageCapture::imageCaptured(int id, const QImage &preview);
336
337 Signal emitted when the frame with request \a id was captured, but not
338 processed and saved yet. Frame \a preview can be displayed to user.
339*/
340
341/*!
342 \fn QImageCapture::imageAvailable(int id, const QVideoFrame &frame)
343
344 Signal emitted when the \a frame with request \a id is available.
345*/
346
347/*!
348 \fn QImageCapture::imageSaved(int id, const QString &fileName)
349
350 Signal emitted when QImageCapture::CaptureToFile is set and
351 the frame with request \a id was saved to \a fileName.
352*/
353
354/*!
355 \enum QImageCapture::FileFormat
356
357 Choose one of the following image formats:
358
359 \value UnspecifiedFormat No format specified
360 \value JPEG \c .jpg or \c .jpeg format
361 \value PNG \c .png format
362 \value WebP \c .webp format
363 \value Tiff \c .tiff format
364 \omitvalue LastFileFormat
365*/
366
367
368/*!
369 \property QImageCapture::fileFormat
370 \brief The image format.
371*/
372
373QImageCapture::FileFormat QImageCapture::fileFormat() const
374{
375 Q_D(const QImageCapture);
376 return d->control ? d->control->imageSettings().format() : UnspecifiedFormat;
377}
378
379/*!
380 Sets the image \a format.
381*/
382void QImageCapture::setFileFormat(QImageCapture::FileFormat format)
383{
384 Q_D(QImageCapture);
385 if (!d->control)
386 return;
387 auto fmt = d->control->imageSettings();
388 if (fmt.format() == format)
389 return;
390 fmt.setFormat(format);
391 d->control->setImageSettings(fmt);
392 emit fileFormatChanged();
393}
394
395/*!
396 Returns a list of supported file formats.
397
398 \sa {QImageCapture::}{FileFormat}
399*/
400QList<QImageCapture::FileFormat> QImageCapture::supportedFormats()
401{
402 return QPlatformMediaIntegration::instance()->formatInfo()->imageFormats;
403}
404
405/*!
406 Returns the name of the given format, \a f.
407*/
408QString QImageCapture::fileFormatName(QImageCapture::FileFormat f)
409{
410 const char *name = nullptr;
411 switch (f) {
412 case UnspecifiedFormat:
413 name = "Unspecified image format";
414 break;
415 case JPEG:
416 name = "JPEG";
417 break;
418 case PNG:
419 name = "PNG";
420 break;
421 case WebP:
422 name = "WebP";
423 break;
424 case Tiff:
425 name = "Tiff";
426 break;
427 }
428 return QString::fromUtf8(utf8: name);
429}
430
431/*!
432 Returns the description of the given file format, \a f.
433*/
434QString QImageCapture::fileFormatDescription(QImageCapture::FileFormat f)
435{
436 const char *name = nullptr;
437 switch (f) {
438 case UnspecifiedFormat:
439 name = "Unspecified image format";
440 break;
441 case JPEG:
442 name = "JPEG";
443 break;
444 case PNG:
445 name = "PNG";
446 break;
447 case WebP:
448 name = "WebP";
449 break;
450 case Tiff:
451 name = "Tiff";
452 break;
453 }
454 return QString::fromUtf8(utf8: name);
455}
456
457/*!
458 Returns the resolution of the encoded image.
459*/
460
461QSize QImageCapture::resolution() const
462{
463 Q_D(const QImageCapture);
464 return d->control ? d->control->imageSettings().resolution() : QSize{};
465}
466
467/*!
468 \fn void QImageCapture::resolutionChanged()
469
470 Signals when the image resolution changes.
471*/
472
473/*!
474 Sets the \a resolution of the encoded image.
475
476 An empty QSize indicates the encoder should make an optimal choice based on
477 what is available from the image source and the limitations of the codec.
478*/
479void QImageCapture::setResolution(const QSize &resolution)
480{
481 Q_D(QImageCapture);
482 if (!d->control)
483 return;
484 auto fmt = d->control->imageSettings();
485 if (fmt.resolution() == resolution)
486 return;
487 fmt.setResolution(resolution);
488 d->control->setImageSettings(fmt);
489 emit resolutionChanged();
490}
491
492/*!
493 Sets the \a width and \a height of the resolution of the encoded image.
494
495 \overload
496*/
497void QImageCapture::setResolution(int width, int height)
498{
499 setResolution(QSize(width, height));
500}
501
502/*!
503 \enum QImageCapture::Quality
504
505 Enumerates quality encoding levels.
506
507 \value VeryLowQuality
508 \value LowQuality
509 \value NormalQuality
510 \value HighQuality
511 \value VeryHighQuality
512*/
513
514/*!
515 \property QImageCapture::quality
516 \brief The image encoding quality.
517*/
518QImageCapture::Quality QImageCapture::quality() const
519{
520 Q_D(const QImageCapture);
521 return d->control ? d->control->imageSettings().quality() : NormalQuality;
522}
523
524/*!
525 Sets the image encoding \a quality.
526*/
527void QImageCapture::setQuality(Quality quality)
528{
529 Q_D(QImageCapture);
530 if (!d->control)
531 return;
532 auto fmt = d->control->imageSettings();
533 if (fmt.quality() == quality)
534 return;
535 fmt.setQuality(quality);
536 d->control->setImageSettings(fmt);
537 emit resolutionChanged();
538}
539
540/*!
541 \internal
542*/
543QPlatformImageCapture *QImageCapture::platformImageCapture()
544{
545 Q_D(QImageCapture);
546 return d->control;
547}
548
549QT_END_NAMESPACE
550
551#include "moc_qimagecapture.cpp"
552

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