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 | |
19 | QT_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 | |
41 | class QImageCapturePrivate |
42 | { |
43 | Q_DECLARE_PUBLIC(QImageCapture) |
44 | public: |
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 | |
61 | void 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 | |
80 | QImageCapture::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 | */ |
120 | void QImageCapture::setCaptureSession(QMediaCaptureSession *session) |
121 | { |
122 | Q_D(QImageCapture); |
123 | d->captureSession = session; |
124 | } |
125 | |
126 | /*! |
127 | Destroys images capture object. |
128 | */ |
129 | |
130 | QImageCapture::~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 | */ |
140 | bool 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 | */ |
152 | QMediaCaptureSession *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 | |
165 | QImageCapture::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 | |
178 | QString 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 | */ |
190 | QMediaMetaData 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 | */ |
200 | void 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 | */ |
213 | void 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 | */ |
229 | bool 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 | */ |
268 | int 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 | */ |
298 | int 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 | |
373 | QImageCapture::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 | */ |
382 | void 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 | */ |
400 | QList<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 | */ |
408 | QString 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 | */ |
434 | QString 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 | |
461 | QSize 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 | */ |
479 | void 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 | */ |
497 | void 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 | */ |
518 | QImageCapture::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 | */ |
527 | void 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 | */ |
543 | QPlatformImageCapture *QImageCapture::platformImageCapture() |
544 | { |
545 | Q_D(QImageCapture); |
546 | return d->control; |
547 | } |
548 | |
549 | QT_END_NAMESPACE |
550 | |
551 | #include "moc_qimagecapture.cpp" |
552 | |