summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp')
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
new file mode 100644
index 000000000..2fb878784
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
@@ -0,0 +1,271 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegimagecapture_p.h"
+#include <private/qplatformmediaformatinfo_p.h>
+#include <private/qplatformcamera_p.h>
+#include <private/qplatformimagecapture_p.h>
+#include <qvideoframeformat.h>
+#include <private/qmediastoragelocation_p.h>
+#include <qimagewriter.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <qstandardpaths.h>
+
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+// Probably, might be increased. To be investigated and tested on Android implementation
+static constexpr int MaxPendingImagesCount = 1;
+
+static Q_LOGGING_CATEGORY(qLcImageCapture, "qt.multimedia.imageCapture")
+
+QFFmpegImageCapture::QFFmpegImageCapture(QImageCapture *parent)
+ : QPlatformImageCapture(parent)
+{
+ qRegisterMetaType<QVideoFrame>();
+}
+
+QFFmpegImageCapture::~QFFmpegImageCapture()
+{
+}
+
+bool QFFmpegImageCapture::isReadyForCapture() const
+{
+ return m_isReadyForCapture;
+}
+
+static const char *extensionForFormat(QImageCapture::FileFormat format)
+{
+ const char *fmt = "jpg";
+ switch (format) {
+ case QImageCapture::UnspecifiedFormat:
+ case QImageCapture::JPEG:
+ fmt = "jpg";
+ break;
+ case QImageCapture::PNG:
+ fmt = "png";
+ break;
+ case QImageCapture::WebP:
+ fmt = "webp";
+ break;
+ case QImageCapture::Tiff:
+ fmt = "tiff";
+ break;
+ }
+ return fmt;
+}
+
+int QFFmpegImageCapture::capture(const QString &fileName)
+{
+ QString path = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String(extensionForFormat(m_settings.format())));
+ return doCapture(path);
+}
+
+int QFFmpegImageCapture::captureToBuffer()
+{
+ return doCapture(QString());
+}
+
+int QFFmpegImageCapture::doCapture(const QString &fileName)
+{
+ qCDebug(qLcImageCapture) << "do capture";
+ if (!m_session) {
+ //emit error in the next event loop,
+ //so application can associate it with returned request id.
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, -1),
+ Q_ARG(int, QImageCapture::ResourceError),
+ Q_ARG(QString, QPlatformImageCapture::msgImageCaptureNotSet()));
+
+ qCDebug(qLcImageCapture) << "error 1";
+ return -1;
+ }
+ if (!m_videoSource) {
+ //emit error in the next event loop,
+ //so application can associate it with returned request id.
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, -1),
+ Q_ARG(int, QImageCapture::ResourceError),
+ Q_ARG(QString,tr("No camera available.")));
+
+ qCDebug(qLcImageCapture) << "error 2";
+ return -1;
+ }
+ if (m_pendingImages.size() >= MaxPendingImagesCount) {
+ //emit error in the next event loop,
+ //so application can associate it with returned request id.
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, -1),
+ Q_ARG(int, QImageCapture::NotReadyError),
+ Q_ARG(QString, QPlatformImageCapture::msgCameraNotReady()));
+
+ qCDebug(qLcImageCapture) << "error 3";
+ return -1;
+ }
+
+ m_lastId++;
+
+ m_pendingImages.enqueue({ m_lastId, fileName, QMediaMetaData{} });
+ updateReadyForCapture();
+
+ return m_lastId;
+}
+
+void QFFmpegImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ auto *captureSession = static_cast<QFFmpegMediaCaptureSession *>(session);
+ if (m_session == captureSession)
+ return;
+
+ if (m_session) {
+ m_session->disconnect(this);
+ m_lastId = 0;
+ m_pendingImages.clear();
+ }
+
+ m_session = captureSession;
+
+ if (m_session)
+ connect(m_session, &QFFmpegMediaCaptureSession::primaryActiveVideoSourceChanged, this,
+ &QFFmpegImageCapture::onVideoSourceChanged);
+
+ onVideoSourceChanged();
+}
+
+void QFFmpegImageCapture::updateReadyForCapture()
+{
+ const bool ready = m_session && m_pendingImages.size() < MaxPendingImagesCount && m_videoSource
+ && m_videoSource->isActive();
+
+ qCDebug(qLcImageCapture) << "updateReadyForCapture" << ready;
+
+ if (std::exchange(m_isReadyForCapture, ready) != ready)
+ emit readyForCaptureChanged(ready);
+}
+
+void QFFmpegImageCapture::newVideoFrame(const QVideoFrame &frame)
+{
+ if (m_pendingImages.empty())
+ return;
+
+ auto pending = m_pendingImages.dequeue();
+
+ qCDebug(qLcImageCapture) << "Taking image" << pending.id;
+
+ emit imageExposed(pending.id);
+ // ### Add metadata from the AVFrame
+ emit imageMetadataAvailable(pending.id, pending.metaData);
+ emit imageAvailable(pending.id, frame);
+ QImage image = frame.toImage();
+ if (m_settings.resolution().isValid() && m_settings.resolution() != image.size())
+ image = image.scaled(m_settings.resolution());
+
+ emit imageCaptured(pending.id, image);
+ if (!pending.filename.isEmpty()) {
+ const char *fmt = nullptr;
+ switch (m_settings.format()) {
+ case QImageCapture::UnspecifiedFormat:
+ case QImageCapture::JPEG:
+ fmt = "jpeg";
+ break;
+ case QImageCapture::PNG:
+ fmt = "png";
+ break;
+ case QImageCapture::WebP:
+ fmt = "webp";
+ break;
+ case QImageCapture::Tiff:
+ fmt = "tiff";
+ break;
+ }
+ int quality = -1;
+ switch (m_settings.quality()) {
+ case QImageCapture::VeryLowQuality:
+ quality = 25;
+ break;
+ case QImageCapture::LowQuality:
+ quality = 50;
+ break;
+ case QImageCapture::NormalQuality:
+ break;
+ case QImageCapture::HighQuality:
+ quality = 75;
+ break;
+ case QImageCapture::VeryHighQuality:
+ quality = 99;
+ break;
+ }
+
+ QImageWriter writer(pending.filename, fmt);
+ writer.setQuality(quality);
+
+ if (writer.write(image)) {
+ emit imageSaved(pending.id, pending.filename);
+ } else {
+ QImageCapture::Error err = QImageCapture::ResourceError;
+ if (writer.error() == QImageWriter::UnsupportedFormatError)
+ err = QImageCapture::FormatError;
+ emit error(pending.id, err, writer.errorString());
+ }
+ }
+
+ updateReadyForCapture();
+}
+
+void QFFmpegImageCapture::setupVideoSourceConnections()
+{
+ connect(m_videoSource, &QPlatformCamera::newVideoFrame, this,
+ &QFFmpegImageCapture::newVideoFrame);
+}
+
+QPlatformVideoSource *QFFmpegImageCapture::videoSource() const
+{
+ return m_videoSource;
+}
+
+void QFFmpegImageCapture::onVideoSourceChanged()
+{
+ if (m_videoSource)
+ m_videoSource->disconnect(this);
+
+ m_videoSource = m_session ? m_session->primaryActiveVideoSource() : nullptr;
+
+ // TODO: optimize, setup the connection only when the capture is ready
+ if (m_videoSource)
+ setupVideoSourceConnections();
+
+ updateReadyForCapture();
+}
+
+QImageEncoderSettings QFFmpegImageCapture::imageSettings() const
+{
+ return m_settings;
+}
+
+void QFFmpegImageCapture::setImageSettings(const QImageEncoderSettings &settings)
+{
+ auto s = settings;
+ const auto supportedFormats = QPlatformMediaIntegration::instance()->formatInfo()->imageFormats;
+ if (supportedFormats.isEmpty()) {
+ emit error(-1, QImageCapture::FormatError, "No image formats supported, can't capture.");
+ return;
+ }
+ if (s.format() == QImageCapture::UnspecifiedFormat) {
+ auto f = QImageCapture::JPEG;
+ if (!supportedFormats.contains(f))
+ f = supportedFormats.first();
+ s.setFormat(f);
+ } else if (!supportedFormats.contains(settings.format())) {
+ emit error(-1, QImageCapture::FormatError, "Image format not supported.");
+ return;
+ }
+
+ m_settings = settings;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegimagecapture_p.cpp"