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 using namespace Qt::Literals::StringLiterals;
75
76 if (!m_session) {
77 //emit error in the next event loop,
78 //so application can associate it with returned request id.
79 QMetaObject::invokeMethod(
80 this,
81 [this]() {
82 emit error(
83 -1,
84 QImageCapture::ResourceError,
85 QPlatformImageCapture::msgImageCaptureNotSet());
86 });
87 return -1;
88 }
89
90 if (!m_videoSource) {
91 //emit error in the next event loop,
92 //so application can associate it with returned request id.
93 QMetaObject::invokeMethod(
94 this,
95 [this]() {
96 emit error(
97 -1,
98 QImageCapture::ResourceError,
99 u"No camera available."_s);
100 });
101 return -1;
102 }
103
104 if (m_pendingImages.size() >= MaxPendingImagesCount) {
105 //emit error in the next event loop,
106 //so application can associate it with returned request id.
107 QMetaObject::invokeMethod(
108 this,
109 [this]() {
110 emit error(
111 -1,
112 QImageCapture::NotReadyError,
113 QPlatformImageCapture::msgCameraNotReady());
114 });
115 return -1;
116 }
117
118 m_lastId++;
119
120 m_pendingImages.enqueue({ m_lastId, fileName, QMediaMetaData{} });
122
123 return m_lastId;
124}
125
126void QFFmpegImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
127{
128 auto *captureSession = static_cast<QFFmpegMediaCaptureSession *>(session);
129 if (m_session == captureSession)
130 return;
131
132 if (m_session) {
133 m_session->disconnect(this);
134 m_lastId = 0;
135 m_pendingImages.clear();
136 }
137
138 m_session = captureSession;
139
140 if (m_session)
141 connect(m_session, &QFFmpegMediaCaptureSession::primaryActiveVideoSourceChanged, this,
142 &QFFmpegImageCapture::onVideoSourceChanged);
143
145}
146
147void QFFmpegImageCapture::cancelPendingImage(QImageCapture::Error error, const QString &errorMsg)
148{
149 if (m_pendingImages.empty()) {
150 qCDebug(qLcImageCapture) <<
151 "QImageCapture backend received an event to cancel a pending capture, "
152 "but no captures are pending. Most likely an internal Qt bug.";
153 return;
154 }
155
156 PendingImage cancelledImage = m_pendingImages.dequeue();
157
158 emit QPlatformImageCapture::error(cancelledImage.id, error, errorMsg);
159
160 updateReadyForCapture();
161}
162
164{
165 const bool ready = m_session && m_pendingImages.size() < MaxPendingImagesCount && m_videoSource
166 && m_videoSource->isActive();
167
168 qCDebug(qLcImageCapture) << "updateReadyForCapture" << ready;
169
170 if (std::exchange(m_isReadyForCapture, ready) != ready)
171 emit readyForCaptureChanged(ready);
172}
173
174void QFFmpegImageCapture::newVideoFrame(const QVideoFrame &frame)
175{
176 if (m_pendingImages.empty())
177 return;
178
179 auto pending = m_pendingImages.dequeue();
180
181 qCDebug(qLcImageCapture) << "Taking image" << pending.id;
182
183 emit imageExposed(pending.id);
184 // ### Add metadata from the AVFrame
185 emit imageMetadataAvailable(pending.id, pending.metaData);
186 emit imageAvailable(pending.id, frame);
187 QImage image = frame.toImage();
188 if (m_settings.resolution().isValid() && m_settings.resolution() != image.size())
189 image = image.scaled(m_settings.resolution());
190
191 emit imageCaptured(pending.id, image);
192 if (!pending.filename.isEmpty()) {
193 const char *fmt = nullptr;
194 switch (m_settings.format()) {
195 case QImageCapture::UnspecifiedFormat:
196 case QImageCapture::JPEG:
197 fmt = "jpeg";
198 break;
199 case QImageCapture::PNG:
200 fmt = "png";
201 break;
202 case QImageCapture::WebP:
203 fmt = "webp";
204 break;
205 case QImageCapture::Tiff:
206 fmt = "tiff";
207 break;
208 }
209 int quality = -1;
210 switch (m_settings.quality()) {
211 case QImageCapture::VeryLowQuality:
212 quality = 25;
213 break;
214 case QImageCapture::LowQuality:
215 quality = 50;
216 break;
217 case QImageCapture::NormalQuality:
218 break;
219 case QImageCapture::HighQuality:
220 quality = 75;
221 break;
222 case QImageCapture::VeryHighQuality:
223 quality = 99;
224 break;
225 }
226
227 QImageWriter writer(pending.filename, fmt);
228 writer.setQuality(quality);
229
230 if (writer.write(image)) {
231 emit imageSaved(pending.id, pending.filename);
232 } else {
233 QImageCapture::Error err = QImageCapture::ResourceError;
234 if (writer.error() == QImageWriter::UnsupportedFormatError)
235 err = QImageCapture::FormatError;
236 emit error(pending.id, err, writer.errorString());
237 }
238 }
239
241}
242
244{
245 connect(m_videoSource, &QPlatformCamera::newVideoFrame, this,
246 &QFFmpegImageCapture::newVideoFrame);
247}
248
250{
251 return m_videoSource;
252}
253
255{
256 if (m_videoSource)
257 m_videoSource->disconnect(this);
258
259 m_videoSource = m_session ? m_session->primaryActiveVideoSource() : nullptr;
260
261 // TODO: optimize, setup the connection only when the capture is ready
262 if (m_videoSource)
264
266}
267
269{
270 return m_settings;
271}
272
273void QFFmpegImageCapture::setImageSettings(const QImageEncoderSettings &settings)
274{
275 auto s = settings;
276 const auto supportedFormats = QPlatformMediaIntegration::instance()->formatInfo()->imageFormats;
277 if (supportedFormats.isEmpty()) {
278 emit error(-1, QImageCapture::FormatError, u"No image formats supported, can't capture."_s);
279 return;
280 }
281 if (s.format() == QImageCapture::UnspecifiedFormat) {
282 auto f = QImageCapture::JPEG;
283 if (!supportedFormats.contains(f))
284 f = supportedFormats.first();
285 s.setFormat(f);
286 } else if (!supportedFormats.contains(settings.format())) {
287 emit error(-1, QImageCapture::FormatError, u"Image format not supported."_s);
288 return;
289 }
290
291 m_settings = settings;
292}
293
294QT_END_NAMESPACE
295
296#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
Combined button and popup list for selecting options.
static constexpr int MaxPendingImagesCount
static const char * extensionForFormat(QImageCapture::FileFormat format)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)