Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qffmpegimagecapture.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
5#include <private/qplatformmediaformatinfo_p.h>
6#include <private/qplatformcamera_p.h>
7#include <private/qplatformimagecapture_p.h>
8#include <qvideoframeformat.h>
9#include <private/qmediastoragelocation_p.h>
10#include <qimagewriter.h>
11
12#include <QtCore/QDebug>
13#include <QtCore/QDir>
14#include <qstandardpaths.h>
15
16#include <qloggingcategory.h>
17
19
20using namespace Qt::StringLiterals;
21
22// Probably, might be increased. To be investigated and tested on Android implementation
23static constexpr int MaxPendingImagesCount = 1;
24
25Q_STATIC_LOGGING_CATEGORY(qLcImageCapture, "qt.multimedia.imageCapture")
26
27QFFmpegImageCapture::QFFmpegImageCapture(QImageCapture *parent)
28 : QPlatformImageCapture(parent)
29{
30 qRegisterMetaType<QVideoFrame>();
31}
32
34
36{
37 return m_isReadyForCapture;
38}
39
40static const char *extensionForFormat(QImageCapture::FileFormat format)
41{
42 const char *fmt = "jpg";
43 switch (format) {
44 case QImageCapture::UnspecifiedFormat:
45 case QImageCapture::JPEG:
46 fmt = "jpg";
47 break;
48 case QImageCapture::PNG:
49 fmt = "png";
50 break;
51 case QImageCapture::WebP:
52 fmt = "webp";
53 break;
54 case QImageCapture::Tiff:
55 fmt = "tiff";
56 break;
57 }
58 return fmt;
59}
60
61int QFFmpegImageCapture::capture(const QString &fileName)
62{
63 QString path = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String(extensionForFormat(m_settings.format())));
64 return doCapture(path);
65}
66
68{
69 return doCapture(QString());
70}
71
72int QFFmpegImageCapture::doCapture(const QString &fileName)
73{
74 qCDebug(qLcImageCapture) << "do capture";
75 if (!m_session) {
76 //emit error in the next event loop,
77 //so application can associate it with returned request id.
78 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
79 Q_ARG(int, -1),
80 Q_ARG(int, QImageCapture::ResourceError),
81 Q_ARG(QString, QPlatformImageCapture::msgImageCaptureNotSet()));
82
83 qCDebug(qLcImageCapture) << "error 1";
84 return -1;
85 }
86 if (!m_videoSource) {
87 //emit error in the next event loop,
88 //so application can associate it with returned request id.
89 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
90 Q_ARG(int, -1),
91 Q_ARG(int, QImageCapture::ResourceError),
92 Q_ARG(QString,tr("No camera available.")));
93
94 qCDebug(qLcImageCapture) << "error 2";
95 return -1;
96 }
97 if (m_pendingImages.size() >= MaxPendingImagesCount) {
98 //emit error in the next event loop,
99 //so application can associate it with returned request id.
100 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
101 Q_ARG(int, -1),
102 Q_ARG(int, QImageCapture::NotReadyError),
103 Q_ARG(QString, QPlatformImageCapture::msgCameraNotReady()));
104
105 qCDebug(qLcImageCapture) << "error 3";
106 return -1;
107 }
108
109 m_lastId++;
110
111 m_pendingImages.enqueue({ m_lastId, fileName, QMediaMetaData{} });
113
114 return m_lastId;
115}
116
117void QFFmpegImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
118{
119 auto *captureSession = static_cast<QFFmpegMediaCaptureSession *>(session);
120 if (m_session == captureSession)
121 return;
122
123 if (m_session) {
124 m_session->disconnect(this);
125 m_lastId = 0;
126 m_pendingImages.clear();
127 }
128
129 m_session = captureSession;
130
131 if (m_session)
132 connect(m_session, &QFFmpegMediaCaptureSession::primaryActiveVideoSourceChanged, this,
133 &QFFmpegImageCapture::onVideoSourceChanged);
134
136}
137
138void QFFmpegImageCapture::cancelPendingImage(QImageCapture::Error error, const QString &errorMsg)
139{
140 if (m_pendingImages.empty()) {
141 qCDebug(qLcImageCapture) <<
142 "QImageCapture backend received an event to cancel a pending capture, "
143 "but no captures are pending. Most likely an internal Qt bug.";
144 return;
145 }
146
147 PendingImage cancelledImage = m_pendingImages.dequeue();
148
149 emit QPlatformImageCapture::error(cancelledImage.id, error, errorMsg);
150
151 updateReadyForCapture();
152}
153
155{
156 const bool ready = m_session && m_pendingImages.size() < MaxPendingImagesCount && m_videoSource
157 && m_videoSource->isActive();
158
159 qCDebug(qLcImageCapture) << "updateReadyForCapture" << ready;
160
161 if (std::exchange(m_isReadyForCapture, ready) != ready)
162 emit readyForCaptureChanged(ready);
163}
164
165void QFFmpegImageCapture::newVideoFrame(const QVideoFrame &frame)
166{
167 if (m_pendingImages.empty())
168 return;
169
170 auto pending = m_pendingImages.dequeue();
171
172 qCDebug(qLcImageCapture) << "Taking image" << pending.id;
173
174 emit imageExposed(pending.id);
175 // ### Add metadata from the AVFrame
176 emit imageMetadataAvailable(pending.id, pending.metaData);
177 emit imageAvailable(pending.id, frame);
178 QImage image = frame.toImage();
179 if (m_settings.resolution().isValid() && m_settings.resolution() != image.size())
180 image = image.scaled(m_settings.resolution());
181
182 emit imageCaptured(pending.id, image);
183 if (!pending.filename.isEmpty()) {
184 const char *fmt = nullptr;
185 switch (m_settings.format()) {
186 case QImageCapture::UnspecifiedFormat:
187 case QImageCapture::JPEG:
188 fmt = "jpeg";
189 break;
190 case QImageCapture::PNG:
191 fmt = "png";
192 break;
193 case QImageCapture::WebP:
194 fmt = "webp";
195 break;
196 case QImageCapture::Tiff:
197 fmt = "tiff";
198 break;
199 }
200 int quality = -1;
201 switch (m_settings.quality()) {
202 case QImageCapture::VeryLowQuality:
203 quality = 25;
204 break;
205 case QImageCapture::LowQuality:
206 quality = 50;
207 break;
208 case QImageCapture::NormalQuality:
209 break;
210 case QImageCapture::HighQuality:
211 quality = 75;
212 break;
213 case QImageCapture::VeryHighQuality:
214 quality = 99;
215 break;
216 }
217
218 QImageWriter writer(pending.filename, fmt);
219 writer.setQuality(quality);
220
221 if (writer.write(image)) {
222 emit imageSaved(pending.id, pending.filename);
223 } else {
224 QImageCapture::Error err = QImageCapture::ResourceError;
225 if (writer.error() == QImageWriter::UnsupportedFormatError)
226 err = QImageCapture::FormatError;
227 emit error(pending.id, err, writer.errorString());
228 }
229 }
230
232}
233
235{
236 connect(m_videoSource, &QPlatformCamera::newVideoFrame, this,
237 &QFFmpegImageCapture::newVideoFrame);
238}
239
241{
242 return m_videoSource;
243}
244
246{
247 if (m_videoSource)
248 m_videoSource->disconnect(this);
249
250 m_videoSource = m_session ? m_session->primaryActiveVideoSource() : nullptr;
251
252 // TODO: optimize, setup the connection only when the capture is ready
253 if (m_videoSource)
255
257}
258
260{
261 return m_settings;
262}
263
264void QFFmpegImageCapture::setImageSettings(const QImageEncoderSettings &settings)
265{
266 auto s = settings;
267 const auto supportedFormats = QPlatformMediaIntegration::instance()->formatInfo()->imageFormats;
268 if (supportedFormats.isEmpty()) {
269 emit error(-1, QImageCapture::FormatError, u"No image formats supported, can't capture."_s);
270 return;
271 }
272 if (s.format() == QImageCapture::UnspecifiedFormat) {
273 auto f = QImageCapture::JPEG;
274 if (!supportedFormats.contains(f))
275 f = supportedFormats.first();
276 s.setFormat(f);
277 } else if (!supportedFormats.contains(settings.format())) {
278 emit error(-1, QImageCapture::FormatError, u"Image format not supported."_s);
279 return;
280 }
281
282 m_settings = settings;
283}
284
285QT_END_NAMESPACE
286
287#include "moc_qffmpegimagecapture_p.cpp"
void setImageSettings(const QImageEncoderSettings &settings) override
virtual int doCapture(const QString &fileName)
QPlatformVideoSource * videoSource() const
QImageEncoderSettings imageSettings() const override
void setCaptureSession(QPlatformMediaCaptureSession *session)
int capture(const QString &fileName) override
~QFFmpegImageCapture() override
virtual void setupVideoSourceConnections()
bool isReadyForCapture() const override
static constexpr int MaxPendingImagesCount
static const char * extensionForFormat(QImageCapture::FileFormat format)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)