1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | //TESTED_COMPONENT=src/multimedia |
30 | |
31 | #include <QtTest/QtTest> |
32 | #include <QtGui/QImageReader> |
33 | #include <QDebug> |
34 | |
35 | #include <qabstractvideosurface.h> |
36 | #include <qcameracontrol.h> |
37 | #include <qcameralockscontrol.h> |
38 | #include <qcameraexposurecontrol.h> |
39 | #include <qcameraflashcontrol.h> |
40 | #include <qcamerafocuscontrol.h> |
41 | #include <qcameraimagecapturecontrol.h> |
42 | #include <qimageencodercontrol.h> |
43 | #include <qcameraimageprocessingcontrol.h> |
44 | #include <qcameracapturebufferformatcontrol.h> |
45 | #include <qcameracapturedestinationcontrol.h> |
46 | #include <qmediaservice.h> |
47 | #include <qcamera.h> |
48 | #include <qcamerainfo.h> |
49 | #include <qcameraimagecapture.h> |
50 | #include <qvideorenderercontrol.h> |
51 | #include <private/qmediaserviceprovider_p.h> |
52 | |
53 | QT_USE_NAMESPACE |
54 | |
55 | /* |
56 | This is the backend conformance test. |
57 | |
58 | Since it relies on platform media framework and sound hardware |
59 | it may be less stable. |
60 | */ |
61 | |
62 | class tst_QCameraBackend: public QObject |
63 | { |
64 | Q_OBJECT |
65 | |
66 | public slots: |
67 | void initTestCase(); |
68 | void cleanupTestCase(); |
69 | |
70 | private slots: |
71 | #if QT_DEPRECATED_SINCE(5, 3) |
72 | void testAvailableDevices(); |
73 | void testDeviceDescription(); |
74 | #endif |
75 | void testCameraInfo(); |
76 | void testCtorWithDevice(); |
77 | void testCtorWithCameraInfo(); |
78 | void testCtorWithPosition(); |
79 | |
80 | void testCameraStates(); |
81 | void testCameraStartError(); |
82 | void testCaptureMode(); |
83 | void testCameraCapture(); |
84 | void testCaptureToBuffer(); |
85 | void testCameraCaptureMetadata(); |
86 | void testExposureCompensation(); |
87 | void testExposureMode(); |
88 | |
89 | void testVideoRecording_data(); |
90 | void testVideoRecording(); |
91 | private: |
92 | }; |
93 | |
94 | void tst_QCameraBackend::initTestCase() |
95 | { |
96 | QCamera camera; |
97 | if (!camera.isAvailable()) |
98 | QSKIP("Camera is not available" ); |
99 | } |
100 | |
101 | void tst_QCameraBackend::cleanupTestCase() |
102 | { |
103 | } |
104 | |
105 | #if QT_DEPRECATED_SINCE(5, 3) |
106 | void tst_QCameraBackend::testAvailableDevices() |
107 | { |
108 | int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(serviceType: QByteArray(Q_MEDIASERVICE_CAMERA)).count(); |
109 | QCOMPARE(QCamera::availableDevices().count(), deviceCount); |
110 | } |
111 | |
112 | void tst_QCameraBackend::testDeviceDescription() |
113 | { |
114 | int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(serviceType: QByteArray(Q_MEDIASERVICE_CAMERA)).count(); |
115 | |
116 | if (deviceCount == 0) |
117 | QVERIFY(QCamera::deviceDescription(QByteArray("random" )).isNull()); |
118 | else { |
119 | const auto devices = QCamera::availableDevices(); |
120 | for (const QByteArray &device : devices) |
121 | QVERIFY(QCamera::deviceDescription(device).length() > 0); |
122 | } |
123 | } |
124 | #endif |
125 | |
126 | void tst_QCameraBackend::testCameraInfo() |
127 | { |
128 | int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(serviceType: QByteArray(Q_MEDIASERVICE_CAMERA)).count(); |
129 | const QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); |
130 | QCOMPARE(cameras.count(), deviceCount); |
131 | if (cameras.isEmpty()) { |
132 | QVERIFY(QCameraInfo::defaultCamera().isNull()); |
133 | QSKIP("Camera selection is not supported" ); |
134 | } |
135 | |
136 | for (const QCameraInfo &info : cameras) { |
137 | QVERIFY(!info.deviceName().isEmpty()); |
138 | QVERIFY(!info.description().isEmpty()); |
139 | QVERIFY(info.orientation() % 90 == 0); |
140 | } |
141 | } |
142 | |
143 | void tst_QCameraBackend::testCtorWithDevice() |
144 | { |
145 | const auto availableCameras = QCameraInfo::availableCameras(); |
146 | if (availableCameras.isEmpty()) |
147 | QSKIP("Camera selection not supported" ); |
148 | |
149 | QCamera *camera = new QCamera(availableCameras.first().deviceName().toLatin1()); |
150 | QCOMPARE(camera->error(), QCamera::NoError); |
151 | delete camera; |
152 | |
153 | //loading non existing camera should fail |
154 | camera = new QCamera(QUuid::createUuid().toByteArray()); |
155 | QCOMPARE(camera->error(), QCamera::ServiceMissingError); |
156 | |
157 | delete camera; |
158 | } |
159 | |
160 | void tst_QCameraBackend::testCtorWithCameraInfo() |
161 | { |
162 | if (QCameraInfo::availableCameras().isEmpty()) |
163 | QSKIP("Camera selection not supported" ); |
164 | |
165 | { |
166 | QCameraInfo info = QCameraInfo::defaultCamera(); |
167 | QCamera camera(info); |
168 | QCOMPARE(camera.error(), QCamera::NoError); |
169 | QCOMPARE(QCameraInfo(camera), info); |
170 | } |
171 | { |
172 | QCameraInfo info = QCameraInfo::availableCameras().first(); |
173 | QCamera camera(info); |
174 | QCOMPARE(camera.error(), QCamera::NoError); |
175 | QCOMPARE(QCameraInfo(camera), info); |
176 | } |
177 | { |
178 | // loading an invalid CameraInfo should fail |
179 | QCamera *camera = new QCamera(QCameraInfo()); |
180 | QCOMPARE(camera->error(), QCamera::ServiceMissingError); |
181 | QVERIFY(QCameraInfo(*camera).isNull()); |
182 | delete camera; |
183 | } |
184 | { |
185 | // loading non existing camera should fail |
186 | QCamera camera(QCameraInfo(QUuid::createUuid().toByteArray())); |
187 | QCOMPARE(camera.error(), QCamera::ServiceMissingError); |
188 | QVERIFY(QCameraInfo(camera).isNull()); |
189 | } |
190 | } |
191 | |
192 | void tst_QCameraBackend::testCtorWithPosition() |
193 | { |
194 | { |
195 | QCamera camera(QCamera::UnspecifiedPosition); |
196 | QCOMPARE(camera.error(), QCamera::NoError); |
197 | } |
198 | { |
199 | QCamera camera(QCamera::FrontFace); |
200 | // even if no camera is available at this position, it should not fail |
201 | // and load the default camera |
202 | QCOMPARE(camera.error(), QCamera::NoError); |
203 | } |
204 | { |
205 | QCamera camera(QCamera::BackFace); |
206 | // even if no camera is available at this position, it should not fail |
207 | // and load the default camera |
208 | QCOMPARE(camera.error(), QCamera::NoError); |
209 | } |
210 | } |
211 | |
212 | void tst_QCameraBackend::testCameraStates() |
213 | { |
214 | QCamera camera; |
215 | QCameraImageCapture imageCapture(&camera); |
216 | |
217 | QSignalSpy errorSignal(&camera, SIGNAL(errorOccurred(QCamera::Error))); |
218 | QSignalSpy stateChangedSignal(&camera, SIGNAL(stateChanged(QCamera::State))); |
219 | QSignalSpy statusChangedSignal(&camera, SIGNAL(statusChanged(QCamera::Status))); |
220 | |
221 | QCOMPARE(camera.state(), QCamera::UnloadedState); |
222 | QCOMPARE(camera.status(), QCamera::UnloadedStatus); |
223 | |
224 | camera.load(); |
225 | QCOMPARE(camera.state(), QCamera::LoadedState); |
226 | QCOMPARE(stateChangedSignal.count(), 1); |
227 | QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::LoadedState); |
228 | QVERIFY(stateChangedSignal.count() > 0); |
229 | |
230 | QTRY_COMPARE(camera.status(), QCamera::LoadedStatus); |
231 | QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::LoadedStatus); |
232 | |
233 | camera.unload(); |
234 | QCOMPARE(camera.state(), QCamera::UnloadedState); |
235 | QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::UnloadedState); |
236 | QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus); |
237 | QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::UnloadedStatus); |
238 | |
239 | camera.start(); |
240 | QCOMPARE(camera.state(), QCamera::ActiveState); |
241 | QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::ActiveState); |
242 | QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); |
243 | QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::ActiveStatus); |
244 | |
245 | camera.stop(); |
246 | QCOMPARE(camera.state(), QCamera::LoadedState); |
247 | QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::LoadedState); |
248 | QTRY_COMPARE(camera.status(), QCamera::LoadedStatus); |
249 | QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::LoadedStatus); |
250 | |
251 | camera.unload(); |
252 | QCOMPARE(camera.state(), QCamera::UnloadedState); |
253 | QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::UnloadedState); |
254 | QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus); |
255 | QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::UnloadedStatus); |
256 | |
257 | QCOMPARE(camera.errorString(), QString()); |
258 | QCOMPARE(errorSignal.count(), 0); |
259 | } |
260 | |
261 | void tst_QCameraBackend::testCameraStartError() |
262 | { |
263 | QCamera camera1(QCameraInfo::defaultCamera()); |
264 | QCamera camera2(QCameraInfo::defaultCamera()); |
265 | QSignalSpy errorSpy1(&camera1, &QCamera::errorOccurred); |
266 | QSignalSpy errorSpy2(&camera2, &QCamera::errorOccurred); |
267 | |
268 | camera1.start(); |
269 | camera2.start(); |
270 | |
271 | QCOMPARE(camera1.state(), QCamera::ActiveState); |
272 | QTRY_COMPARE(camera1.status(), QCamera::ActiveStatus); |
273 | QCOMPARE(camera1.error(), QCamera::NoError); |
274 | QCOMPARE(camera2.state(), QCamera::UnloadedState); |
275 | QCOMPARE(camera2.status(), QCamera::UnloadedStatus); |
276 | QCOMPARE(camera2.error(), QCamera::CameraError); |
277 | |
278 | QCOMPARE(errorSpy1.count(), 0); |
279 | QCOMPARE(errorSpy2.count(), 1); |
280 | } |
281 | |
282 | void tst_QCameraBackend::testCaptureMode() |
283 | { |
284 | QCamera camera; |
285 | |
286 | QSignalSpy errorSignal(&camera, SIGNAL(errorOccurred(QCamera::Error))); |
287 | QSignalSpy stateChangedSignal(&camera, SIGNAL(stateChanged(QCamera::State))); |
288 | QSignalSpy captureModeSignal(&camera, SIGNAL(captureModeChanged(QCamera::CaptureModes))); |
289 | |
290 | QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage); |
291 | |
292 | if (!camera.isCaptureModeSupported(mode: QCamera::CaptureVideo)) { |
293 | camera.setCaptureMode(QCamera::CaptureVideo); |
294 | QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage); |
295 | QSKIP("Video capture not supported" ); |
296 | } |
297 | |
298 | camera.setCaptureMode(QCamera::CaptureVideo); |
299 | QCOMPARE(camera.captureMode(), QCamera::CaptureVideo); |
300 | QTRY_COMPARE(captureModeSignal.size(), 1); |
301 | QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureModes>(), QCamera::CaptureVideo); |
302 | captureModeSignal.clear(); |
303 | |
304 | camera.load(); |
305 | QTRY_COMPARE(camera.status(), QCamera::LoadedStatus); |
306 | //capture mode should still be video |
307 | QCOMPARE(camera.captureMode(), QCamera::CaptureVideo); |
308 | |
309 | //it should be possible to switch capture mode in Loaded state |
310 | camera.setCaptureMode(QCamera::CaptureStillImage); |
311 | QTRY_COMPARE(captureModeSignal.size(), 1); |
312 | QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureModes>(), QCamera::CaptureStillImage); |
313 | captureModeSignal.clear(); |
314 | |
315 | camera.setCaptureMode(QCamera::CaptureVideo); |
316 | QTRY_COMPARE(captureModeSignal.size(), 1); |
317 | QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureModes>(), QCamera::CaptureVideo); |
318 | captureModeSignal.clear(); |
319 | |
320 | camera.start(); |
321 | QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); |
322 | //capture mode should still be video |
323 | QCOMPARE(camera.captureMode(), QCamera::CaptureVideo); |
324 | |
325 | stateChangedSignal.clear(); |
326 | //it should be possible to switch capture mode in Active state |
327 | camera.setCaptureMode(QCamera::CaptureStillImage); |
328 | //camera may leave Active status, but should return to Active |
329 | QTest::qWait(ms: 10); //camera may leave Active status async |
330 | QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); |
331 | QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage); |
332 | QVERIFY2(stateChangedSignal.isEmpty(), "camera should not change the state during capture mode changes" ); |
333 | |
334 | QCOMPARE(captureModeSignal.size(), 1); |
335 | QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureModes>(), QCamera::CaptureStillImage); |
336 | captureModeSignal.clear(); |
337 | |
338 | camera.setCaptureMode(QCamera::CaptureVideo); |
339 | //camera may leave Active status, but should return to Active |
340 | QTest::qWait(ms: 10); //camera may leave Active status async |
341 | QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); |
342 | QCOMPARE(camera.captureMode(), QCamera::CaptureVideo); |
343 | |
344 | QVERIFY2(stateChangedSignal.isEmpty(), "camera should not change the state during capture mode changes" ); |
345 | |
346 | QCOMPARE(captureModeSignal.size(), 1); |
347 | QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureModes>(), QCamera::CaptureVideo); |
348 | captureModeSignal.clear(); |
349 | |
350 | camera.stop(); |
351 | QCOMPARE(camera.captureMode(), QCamera::CaptureVideo); |
352 | camera.unload(); |
353 | QCOMPARE(camera.captureMode(), QCamera::CaptureVideo); |
354 | |
355 | QVERIFY2(errorSignal.isEmpty(), QString("Camera error: %1" ).arg(camera.errorString()).toLocal8Bit()); |
356 | } |
357 | |
358 | void tst_QCameraBackend::testCameraCapture() |
359 | { |
360 | QCamera camera; |
361 | QCameraImageCapture imageCapture(&camera); |
362 | //prevents camera to flash during the test |
363 | camera.exposure()->setFlashMode(QCameraExposure::FlashOff); |
364 | |
365 | QVERIFY(!imageCapture.isReadyForCapture()); |
366 | |
367 | QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage))); |
368 | QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); |
369 | QSignalSpy errorSignal(&imageCapture, SIGNAL(error(int,QCameraImageCapture::Error,QString))); |
370 | |
371 | imageCapture.capture(); |
372 | QTRY_COMPARE(errorSignal.size(), 1); |
373 | QCOMPARE(imageCapture.error(), QCameraImageCapture::NotReadyError); |
374 | QCOMPARE(capturedSignal.size(), 0); |
375 | errorSignal.clear(); |
376 | |
377 | camera.start(); |
378 | |
379 | QTRY_VERIFY(imageCapture.isReadyForCapture()); |
380 | QCOMPARE(camera.status(), QCamera::ActiveStatus); |
381 | QCOMPARE(errorSignal.size(), 0); |
382 | |
383 | int id = imageCapture.capture(); |
384 | |
385 | QTRY_VERIFY(!savedSignal.isEmpty()); |
386 | |
387 | QTRY_COMPARE(capturedSignal.size(), 1); |
388 | QCOMPARE(capturedSignal.last().first().toInt(), id); |
389 | QCOMPARE(errorSignal.size(), 0); |
390 | QCOMPARE(imageCapture.error(), QCameraImageCapture::NoError); |
391 | |
392 | QCOMPARE(savedSignal.last().first().toInt(), id); |
393 | QString location = savedSignal.last().last().toString(); |
394 | QVERIFY(!location.isEmpty()); |
395 | QVERIFY(QFileInfo(location).exists()); |
396 | QImageReader reader(location); |
397 | reader.setScaledSize(QSize(320,240)); |
398 | QVERIFY(!reader.read().isNull()); |
399 | |
400 | QFile(location).remove(); |
401 | } |
402 | |
403 | |
404 | void tst_QCameraBackend::testCaptureToBuffer() |
405 | { |
406 | QCamera camera; |
407 | QCameraImageCapture imageCapture(&camera); |
408 | camera.exposure()->setFlashMode(QCameraExposure::FlashOff); |
409 | |
410 | camera.load(); |
411 | |
412 | if (!imageCapture.isCaptureDestinationSupported(destination: QCameraImageCapture::CaptureToBuffer)) |
413 | QSKIP("Buffer capture not supported" ); |
414 | |
415 | QTRY_COMPARE(camera.status(), QCamera::LoadedStatus); |
416 | |
417 | QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToFile)); |
418 | QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer)); |
419 | QVERIFY(imageCapture.isCaptureDestinationSupported( |
420 | QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile)); |
421 | |
422 | QSignalSpy destinationChangedSignal(&imageCapture, SIGNAL(captureDestinationChanged(QCameraImageCapture::CaptureDestinations))); |
423 | |
424 | QCOMPARE(imageCapture.captureDestination(), QCameraImageCapture::CaptureToFile); |
425 | imageCapture.setCaptureDestination(QCameraImageCapture::CaptureToBuffer); |
426 | QCOMPARE(imageCapture.captureDestination(), QCameraImageCapture::CaptureToBuffer); |
427 | QCOMPARE(destinationChangedSignal.size(), 1); |
428 | QCOMPARE(destinationChangedSignal.first().first().value<QCameraImageCapture::CaptureDestinations>(), |
429 | QCameraImageCapture::CaptureToBuffer); |
430 | |
431 | QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage))); |
432 | QSignalSpy imageAvailableSignal(&imageCapture, SIGNAL(imageAvailable(int,QVideoFrame))); |
433 | QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); |
434 | QSignalSpy errorSignal(&imageCapture, SIGNAL(error(int,QCameraImageCapture::Error,QString))); |
435 | |
436 | camera.start(); |
437 | QTRY_VERIFY(imageCapture.isReadyForCapture()); |
438 | |
439 | int id = imageCapture.capture(); |
440 | QTRY_VERIFY(!imageAvailableSignal.isEmpty()); |
441 | |
442 | QVERIFY(errorSignal.isEmpty()); |
443 | QTRY_VERIFY(!capturedSignal.isEmpty()); |
444 | QVERIFY(!imageAvailableSignal.isEmpty()); |
445 | |
446 | QTest::qWait(ms: 2000); |
447 | QVERIFY(savedSignal.isEmpty()); |
448 | |
449 | QCOMPARE(capturedSignal.first().first().toInt(), id); |
450 | QCOMPARE(imageAvailableSignal.first().first().toInt(), id); |
451 | |
452 | QVideoFrame frame = imageAvailableSignal.first().last().value<QVideoFrame>(); |
453 | QVERIFY(!frame.image().isNull()); |
454 | |
455 | frame = QVideoFrame(); |
456 | capturedSignal.clear(); |
457 | imageAvailableSignal.clear(); |
458 | savedSignal.clear(); |
459 | |
460 | if (imageCapture.supportedBufferFormats().contains(t: QVideoFrame::Format_UYVY)) { |
461 | imageCapture.setBufferFormat(QVideoFrame::Format_UYVY); |
462 | QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_UYVY); |
463 | |
464 | id = imageCapture.capture(); |
465 | QTRY_VERIFY(!imageAvailableSignal.isEmpty()); |
466 | |
467 | QVERIFY(errorSignal.isEmpty()); |
468 | QVERIFY(!capturedSignal.isEmpty()); |
469 | QVERIFY(!imageAvailableSignal.isEmpty()); |
470 | QVERIFY(savedSignal.isEmpty()); |
471 | |
472 | QTest::qWait(ms: 2000); |
473 | QVERIFY(savedSignal.isEmpty()); |
474 | |
475 | frame = imageAvailableSignal.first().last().value<QVideoFrame>(); |
476 | QVERIFY(frame.isValid()); |
477 | |
478 | qDebug() << frame.pixelFormat(); |
479 | QCOMPARE(frame.pixelFormat(), QVideoFrame::Format_UYVY); |
480 | QVERIFY(!frame.size().isEmpty()); |
481 | frame = QVideoFrame(); |
482 | |
483 | capturedSignal.clear(); |
484 | imageAvailableSignal.clear(); |
485 | savedSignal.clear(); |
486 | |
487 | imageCapture.setBufferFormat(QVideoFrame::Format_Jpeg); |
488 | QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_Jpeg); |
489 | } |
490 | |
491 | QTRY_VERIFY(imageCapture.isReadyForCapture()); |
492 | |
493 | //Try to capture to both buffer and file |
494 | if (imageCapture.isCaptureDestinationSupported(destination: QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile)) { |
495 | imageCapture.setCaptureDestination(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile); |
496 | |
497 | int oldId = id; |
498 | id = imageCapture.capture(); |
499 | QVERIFY(id != oldId); |
500 | QTRY_VERIFY(!savedSignal.isEmpty()); |
501 | |
502 | QVERIFY(errorSignal.isEmpty()); |
503 | QVERIFY(!capturedSignal.isEmpty()); |
504 | QVERIFY(!imageAvailableSignal.isEmpty()); |
505 | QVERIFY(!savedSignal.isEmpty()); |
506 | |
507 | QCOMPARE(capturedSignal.first().first().toInt(), id); |
508 | QCOMPARE(imageAvailableSignal.first().first().toInt(), id); |
509 | |
510 | frame = imageAvailableSignal.first().last().value<QVideoFrame>(); |
511 | QVERIFY(!frame.image().isNull()); |
512 | |
513 | QString fileName = savedSignal.first().last().toString(); |
514 | QVERIFY(QFileInfo(fileName).exists()); |
515 | } |
516 | } |
517 | |
518 | void tst_QCameraBackend::testCameraCaptureMetadata() |
519 | { |
520 | QSKIP("Capture metadata is supported only on harmattan" ); |
521 | |
522 | QCamera camera; |
523 | QCameraImageCapture imageCapture(&camera); |
524 | camera.exposure()->setFlashMode(QCameraExposure::FlashOff); |
525 | |
526 | QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,QString,QVariant))); |
527 | QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); |
528 | |
529 | camera.start(); |
530 | |
531 | QTRY_VERIFY(imageCapture.isReadyForCapture()); |
532 | |
533 | int id = imageCapture.capture(location: QString::fromLatin1(str: "/dev/null" )); |
534 | QTRY_VERIFY(!savedSignal.isEmpty()); |
535 | QVERIFY(!metadataSignal.isEmpty()); |
536 | QCOMPARE(metadataSignal.first().first().toInt(), id); |
537 | } |
538 | |
539 | void tst_QCameraBackend::testExposureCompensation() |
540 | { |
541 | QSKIP("Capture exposure parameters are supported only on mobile platforms" ); |
542 | |
543 | QCamera camera; |
544 | QCameraExposure *exposure = camera.exposure(); |
545 | |
546 | QSignalSpy exposureCompensationSignal(exposure, SIGNAL(exposureCompensationChanged(qreal))); |
547 | |
548 | //it should be possible to set exposure parameters in Unloaded state |
549 | QCOMPARE(exposure->exposureCompensation()+1.0, 1.0); |
550 | exposure->setExposureCompensation(1.0); |
551 | QCOMPARE(exposure->exposureCompensation(), 1.0); |
552 | QTRY_COMPARE(exposureCompensationSignal.count(), 1); |
553 | QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0); |
554 | |
555 | //exposureCompensationChanged should not be emitted when value is not changed |
556 | exposure->setExposureCompensation(1.0); |
557 | QTest::qWait(ms: 50); |
558 | QCOMPARE(exposureCompensationSignal.count(), 1); |
559 | |
560 | //exposure compensation should be preserved during load/start |
561 | camera.load(); |
562 | QTRY_COMPARE(camera.status(), QCamera::LoadedStatus); |
563 | |
564 | QCOMPARE(exposure->exposureCompensation(), 1.0); |
565 | |
566 | exposureCompensationSignal.clear(); |
567 | exposure->setExposureCompensation(-1.0); |
568 | QCOMPARE(exposure->exposureCompensation(), -1.0); |
569 | QTRY_COMPARE(exposureCompensationSignal.count(), 1); |
570 | QCOMPARE(exposureCompensationSignal.last().first().toReal(), -1.0); |
571 | |
572 | camera.start(); |
573 | QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); |
574 | |
575 | QCOMPARE(exposure->exposureCompensation(), -1.0); |
576 | |
577 | exposureCompensationSignal.clear(); |
578 | exposure->setExposureCompensation(1.0); |
579 | QCOMPARE(exposure->exposureCompensation(), 1.0); |
580 | QTRY_COMPARE(exposureCompensationSignal.count(), 1); |
581 | QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0); |
582 | } |
583 | |
584 | void tst_QCameraBackend::testExposureMode() |
585 | { |
586 | QSKIP("Capture exposure parameters are supported only on mobile platforms" ); |
587 | |
588 | QCamera camera; |
589 | QCameraExposure *exposure = camera.exposure(); |
590 | |
591 | QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto); |
592 | |
593 | // Night |
594 | exposure->setExposureMode(QCameraExposure::ExposureNight); |
595 | QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureNight); |
596 | camera.start(); |
597 | QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); |
598 | QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureNight); |
599 | |
600 | camera.unload(); |
601 | QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus); |
602 | |
603 | // Auto |
604 | exposure->setExposureMode(QCameraExposure::ExposureAuto); |
605 | QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto); |
606 | camera.start(); |
607 | QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); |
608 | QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto); |
609 | } |
610 | |
611 | void tst_QCameraBackend::testVideoRecording_data() |
612 | { |
613 | QTest::addColumn<QByteArray>(name: "device" ); |
614 | |
615 | const auto devices = QCameraInfo::availableCameras(); |
616 | |
617 | for (const auto &device : devices) { |
618 | QTest::newRow(dataTag: device.description().toUtf8()) |
619 | << device.deviceName().toLatin1(); |
620 | } |
621 | |
622 | if (devices.isEmpty()) |
623 | QTest::newRow(dataTag: "Default device" ) << QByteArray(); |
624 | } |
625 | |
626 | void tst_QCameraBackend::testVideoRecording() |
627 | { |
628 | QFETCH(QByteArray, device); |
629 | |
630 | QScopedPointer<QCamera> camera(device.isEmpty() ? new QCamera : new QCamera(device)); |
631 | |
632 | QMediaRecorder recorder(camera.data()); |
633 | |
634 | QSignalSpy errorSignal(camera.data(), SIGNAL(errorOccurred(QCamera::Error))); |
635 | QSignalSpy recorderErrorSignal(&recorder, SIGNAL(error(QMediaRecorder::Error))); |
636 | QSignalSpy recorderStatusSignal(&recorder, SIGNAL(statusChanged(QMediaRecorder::Status))); |
637 | |
638 | if (!camera->isCaptureModeSupported(mode: QCamera::CaptureVideo)) { |
639 | QSKIP("Video capture not supported" ); |
640 | } |
641 | |
642 | camera->setCaptureMode(QCamera::CaptureVideo); |
643 | |
644 | QVideoEncoderSettings videoSettings; |
645 | videoSettings.setResolution(width: 320, height: 240); |
646 | recorder.setVideoSettings(videoSettings); |
647 | |
648 | QCOMPARE(recorder.status(), QMediaRecorder::UnloadedStatus); |
649 | |
650 | camera->start(); |
651 | QVERIFY(recorder.status() == QMediaRecorder::LoadingStatus || |
652 | recorder.status() == QMediaRecorder::LoadedStatus); |
653 | QCOMPARE(recorderStatusSignal.last().first().value<QMediaRecorder::Status>(), recorder.status()); |
654 | QTRY_COMPARE(camera->status(), QCamera::ActiveStatus); |
655 | QTRY_COMPARE(recorder.status(), QMediaRecorder::LoadedStatus); |
656 | QCOMPARE(recorderStatusSignal.last().first().value<QMediaRecorder::Status>(), recorder.status()); |
657 | |
658 | //record 5 seconds clip |
659 | recorder.record(); |
660 | QTRY_COMPARE(recorder.status(), QMediaRecorder::RecordingStatus); |
661 | QCOMPARE(recorderStatusSignal.last().first().value<QMediaRecorder::Status>(), recorder.status()); |
662 | QTest::qWait(ms: 5000); |
663 | recorderStatusSignal.clear(); |
664 | recorder.stop(); |
665 | bool foundFinalizingStatus = false; |
666 | for (auto &list : recorderStatusSignal) { |
667 | if (list.contains(t: QVariant(QMediaRecorder::FinalizingStatus))) { |
668 | foundFinalizingStatus = true; |
669 | break; |
670 | } |
671 | } |
672 | QVERIFY(foundFinalizingStatus); |
673 | QTRY_COMPARE(recorder.status(), QMediaRecorder::LoadedStatus); |
674 | QCOMPARE(recorderStatusSignal.last().first().value<QMediaRecorder::Status>(), recorder.status()); |
675 | |
676 | QVERIFY(errorSignal.isEmpty()); |
677 | QVERIFY(recorderErrorSignal.isEmpty()); |
678 | |
679 | QString fileName = recorder.actualLocation().toLocalFile(); |
680 | QVERIFY(!fileName.isEmpty()); |
681 | |
682 | QVERIFY(QFileInfo(fileName).size() > 0); |
683 | QFile(fileName).remove(); |
684 | |
685 | camera->setCaptureMode(QCamera::CaptureStillImage); |
686 | QTRY_COMPARE(recorder.status(), QMediaRecorder::UnloadedStatus); |
687 | QCOMPARE(recorderStatusSignal.last().first().value<QMediaRecorder::Status>(), recorder.status()); |
688 | } |
689 | |
690 | QTEST_MAIN(tst_QCameraBackend) |
691 | |
692 | #include "tst_qcamerabackend.moc" |
693 | |