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