/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/multimedia #include #include #include #include #include #include #include #include #include #include #include #include #include QT_USE_NAMESPACE /* This is the backend conformance test. Since it relies on platform media framework and sound hardware it may be less stable. */ class tst_QCameraBackend: public QObject { Q_OBJECT public slots: void initTestCase(); void cleanupTestCase(); private slots: void testCameraInfo(); void testCtorWithCameraInfo(); void testCtorWithPosition(); void testCameraStates(); void testCameraStartParallel(); void testCameraCapture(); void testCaptureToBuffer(); void testCameraCaptureMetadata(); void testExposureCompensation(); void testExposureMode(); void testVideoRecording_data(); void testVideoRecording(); private: bool noCamera = false; }; void tst_QCameraBackend::initTestCase() { QCamera camera; noCamera = !camera.isAvailable(); } void tst_QCameraBackend::cleanupTestCase() { } void tst_QCameraBackend::testCameraInfo() { const QList cameras = QMediaDevices::videoInputs(); if (cameras.isEmpty()) { QVERIFY(noCamera); QVERIFY(QMediaDevices::defaultVideoInput().isNull()); QSKIP("Camera selection is not supported"); } QVERIFY(!noCamera); for (const QCameraInfo &info : cameras) { QVERIFY(!info.id().isEmpty()); QVERIFY(!info.description().isEmpty()); } } void tst_QCameraBackend::testCtorWithCameraInfo() { { // loading an invalid CameraInfo should fail QCamera camera(QCameraInfo{}); QCOMPARE(camera.error(), QCamera::CameraError); QVERIFY(camera.cameraInfo().isNull()); } if (noCamera) QSKIP("No camera available"); { QCameraInfo info = QMediaDevices::defaultVideoInput(); QCamera camera(info); QCOMPARE(camera.error(), QCamera::NoError); QCOMPARE(camera.cameraInfo(), info); } { QCameraInfo info = QMediaDevices::videoInputs().first(); QCamera camera(info); QCOMPARE(camera.error(), QCamera::NoError); QCOMPARE(camera.cameraInfo(), info); } } void tst_QCameraBackend::testCtorWithPosition() { if (noCamera) QSKIP("No camera available"); { QCamera camera(QCameraInfo::UnspecifiedPosition); QCOMPARE(camera.error(), QCamera::NoError); } { QCamera camera(QCameraInfo::FrontFace); // even if no camera is available at this position, it should not fail // and load the default camera QCOMPARE(camera.error(), QCamera::NoError); } { QCamera camera(QCameraInfo::BackFace); // even if no camera is available at this position, it should not fail // and load the default camera QCOMPARE(camera.error(), QCamera::NoError); } } void tst_QCameraBackend::testCameraStates() { QMediaCaptureSession session; QCamera camera; camera.setCameraInfo(QCameraInfo()); QCameraImageCapture imageCapture; session.setCamera(&camera); session.setImageCapture(&imageCapture); QSignalSpy errorSignal(&camera, SIGNAL(errorOccurred(QCamera::Error, const QString &))); QSignalSpy activeChangedSignal(&camera, SIGNAL(activeChanged(bool))); QSignalSpy statusChangedSignal(&camera, SIGNAL(statusChanged(QCamera::Status))); QCOMPARE(camera.isActive(), false); QCOMPARE(camera.status(), QCamera::InactiveStatus); camera.start(); QCOMPARE(camera.isActive(), false); if (noCamera) QSKIP("No camera available"); camera.setCameraInfo(QMediaDevices::defaultVideoInput()); QCOMPARE(camera.error(), QCamera::NoError); camera.start(); QCOMPARE(camera.isActive(), true); QTRY_COMPARE(activeChangedSignal.size(), 1); QCOMPARE(activeChangedSignal.last().first().value(), true); QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); QCOMPARE(statusChangedSignal.last().first().value(), QCamera::ActiveStatus); camera.stop(); QCOMPARE(camera.isActive(), false); QCOMPARE(activeChangedSignal.last().first().value(), false); QTRY_COMPARE(camera.status(), QCamera::InactiveStatus); QCOMPARE(statusChangedSignal.last().first().value(), QCamera::InactiveStatus); QCOMPARE(camera.errorString(), QString()); } void tst_QCameraBackend::testCameraStartParallel() { if (noCamera) QSKIP("No camera available"); QMediaCaptureSession session1; QMediaCaptureSession session2; QCamera camera1(QMediaDevices::defaultVideoInput()); QCamera camera2(QMediaDevices::defaultVideoInput()); session1.setCamera(&camera1); session2.setCamera(&camera2); QSignalSpy errorSpy1(&camera1, &QCamera::errorOccurred); QSignalSpy errorSpy2(&camera2, &QCamera::errorOccurred); camera1.start(); camera2.start(); QCOMPARE(camera1.isActive(), true); QTRY_COMPARE(camera1.status(), QCamera::ActiveStatus); QCOMPARE(camera1.error(), QCamera::NoError); QCOMPARE(camera2.isActive(), true); QCOMPARE(camera2.error(), QCamera::NoError); QCOMPARE(errorSpy1.count(), 0); QCOMPARE(errorSpy2.count(), 0); } void tst_QCameraBackend::testCameraCapture() { QMediaCaptureSession session; QCamera camera; QCameraImageCapture imageCapture; session.setCamera(&camera); session.setImageCapture(&imageCapture); //prevents camera to flash during the test camera.setFlashMode(QCamera::FlashOff); QVERIFY(!imageCapture.isReadyForCapture()); QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage))); QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QCameraImageCapture::Error,const QString&))); imageCapture.captureToFile(); QTRY_COMPARE(errorSignal.size(), 1); QCOMPARE(imageCapture.error(), QCameraImageCapture::NotReadyError); QCOMPARE(capturedSignal.size(), 0); errorSignal.clear(); if (noCamera) QSKIP("No camera available"); camera.start(); QTRY_VERIFY(imageCapture.isReadyForCapture()); QCOMPARE(camera.status(), QCamera::ActiveStatus); QCOMPARE(errorSignal.size(), 0); int id = imageCapture.captureToFile(); QTRY_VERIFY(!savedSignal.isEmpty()); QTRY_COMPARE(capturedSignal.size(), 1); QCOMPARE(capturedSignal.last().first().toInt(), id); QCOMPARE(errorSignal.size(), 0); QCOMPARE(imageCapture.error(), QCameraImageCapture::NoError); QCOMPARE(savedSignal.last().first().toInt(), id); QString location = savedSignal.last().last().toString(); QVERIFY(!location.isEmpty()); QVERIFY(QFileInfo(location).exists()); QImageReader reader(location); reader.setScaledSize(QSize(320,240)); QVERIFY(!reader.read().isNull()); QFile(location).remove(); } void tst_QCameraBackend::testCaptureToBuffer() { if (noCamera) QSKIP("No camera available"); QMediaCaptureSession session; QCamera camera; QCameraImageCapture imageCapture; session.setCamera(&camera); session.setImageCapture(&imageCapture); camera.setFlashMode(QCamera::FlashOff); camera.setActive(true); QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage))); QSignalSpy imageAvailableSignal(&imageCapture, SIGNAL(imageAvailable(int,QVideoFrame))); QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QCameraImageCapture::Error,const QString&))); camera.start(); QTRY_VERIFY(imageCapture.isReadyForCapture()); int id = imageCapture.capture(); QTRY_VERIFY(!imageAvailableSignal.isEmpty()); QVERIFY(errorSignal.isEmpty()); QTRY_VERIFY(!capturedSignal.isEmpty()); QVERIFY(!imageAvailableSignal.isEmpty()); QTest::qWait(2000); QVERIFY(savedSignal.isEmpty()); QCOMPARE(capturedSignal.first().first().toInt(), id); QCOMPARE(imageAvailableSignal.first().first().toInt(), id); QVideoFrame frame = imageAvailableSignal.first().last().value(); QVERIFY(!frame.toImage().isNull()); frame = QVideoFrame(); capturedSignal.clear(); imageAvailableSignal.clear(); savedSignal.clear(); QTRY_VERIFY(imageCapture.isReadyForCapture()); } void tst_QCameraBackend::testCameraCaptureMetadata() { if (noCamera) QSKIP("No camera available"); QMediaCaptureSession session; QCamera camera; QCameraImageCapture imageCapture; session.setCamera(&camera); session.setImageCapture(&imageCapture); camera.setFlashMode(QCamera::FlashOff); QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,const QMediaMetaData&))); QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); camera.start(); QTRY_VERIFY(imageCapture.isReadyForCapture()); int id = imageCapture.captureToFile(QString::fromLatin1("/dev/null")); QTRY_VERIFY(!savedSignal.isEmpty()); QVERIFY(!metadataSignal.isEmpty()); QCOMPARE(metadataSignal.first().first().toInt(), id); } void tst_QCameraBackend::testExposureCompensation() { if (noCamera) QSKIP("No camera available"); QMediaCaptureSession session; QCamera camera; session.setCamera(&camera); QSignalSpy exposureCompensationSignal(&camera, SIGNAL(exposureCompensationChanged(qreal))); //it should be possible to set exposure parameters in Unloaded state QCOMPARE(camera.exposureCompensation()+1.0, 1.0); camera.setExposureCompensation(1.0); QCOMPARE(camera.exposureCompensation(), 1.0); QTRY_COMPARE(exposureCompensationSignal.count(), 1); QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0); //exposureCompensationChanged should not be emitted when value is not changed camera.setExposureCompensation(1.0); QTest::qWait(50); QCOMPARE(exposureCompensationSignal.count(), 1); //exposure compensation should be preserved during start camera.start(); QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); QCOMPARE(camera.exposureCompensation(), -1.0); exposureCompensationSignal.clear(); camera.setExposureCompensation(1.0); QCOMPARE(camera.exposureCompensation(), 1.0); QTRY_COMPARE(exposureCompensationSignal.count(), 1); QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0); } void tst_QCameraBackend::testExposureMode() { if (noCamera) QSKIP("No camera available"); QCamera camera; QCOMPARE(camera.exposureMode(), QCamera::ExposureAuto); // Night camera.setExposureMode(QCamera::ExposureNight); QCOMPARE(camera.exposureMode(), QCamera::ExposureNight); camera.start(); QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); QCOMPARE(camera.exposureMode(), QCamera::ExposureNight); camera.stop(); QTRY_COMPARE(camera.status(), QCamera::InactiveStatus); // Auto camera.setExposureMode(QCamera::ExposureAuto); QCOMPARE(camera.exposureMode(), QCamera::ExposureAuto); camera.start(); QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); QCOMPARE(camera.exposureMode(), QCamera::ExposureAuto); } void tst_QCameraBackend::testVideoRecording_data() { QTest::addColumn("device"); const auto devices = QMediaDevices::videoInputs(); for (const auto &device : devices) QTest::newRow(device.description().toUtf8()) << device; if (devices.isEmpty()) QTest::newRow("Null device") << QCameraInfo(); } void tst_QCameraBackend::testVideoRecording() { QFETCH(QCameraInfo, device); QMediaCaptureSession session; QScopedPointer camera(new QCamera(device)); session.setCamera(camera.data()); QMediaEncoder recorder; session.setEncoder(&recorder); QSignalSpy errorSignal(camera.data(), SIGNAL(errorOccurred(QCamera::Error, const QString &))); QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(QMediaEncoder::Error, const QString &))); QSignalSpy recorderStatusSignal(&recorder, SIGNAL(statusChanged(QMediaEncoder::Status))); QMediaEncoderSettings videoSettings; videoSettings.setVideoResolution(320, 240); recorder.setEncoderSettings(videoSettings); QCOMPARE(recorder.status(), QMediaEncoder::StoppedStatus); camera->start(); if (noCamera || device.isNull()) { QVERIFY(!camera->isActive()); return; } QTRY_VERIFY(camera->isActive()); QTRY_COMPARE(camera->status(), QCamera::ActiveStatus); QTRY_COMPARE(recorder.status(), QMediaEncoder::StoppedStatus); //record 1 seconds clip recorder.record(); QTRY_COMPARE(recorder.status(), QMediaEncoder::RecordingStatus); QCOMPARE(recorderStatusSignal.last().first().value(), recorder.status()); QTest::qWait(1000); recorderStatusSignal.clear(); recorder.stop(); bool foundFinalizingStatus = false; for (auto &list : recorderStatusSignal) { if (qvariant_cast(list.first()) == QMediaEncoder::FinalizingStatus) { foundFinalizingStatus = true; break; } } QVERIFY(foundFinalizingStatus); QTRY_COMPARE(recorder.status(), QMediaEncoder::StoppedStatus); QCOMPARE(recorderStatusSignal.last().first().value(), recorder.status()); QVERIFY(errorSignal.isEmpty()); QVERIFY(recorderErrorSignal.isEmpty()); QString fileName = recorder.actualLocation().toLocalFile(); QVERIFY(!fileName.isEmpty()); QVERIFY(QFileInfo(fileName).size() > 0); QFile(fileName).remove(); QTRY_COMPARE(recorder.status(), QMediaEncoder::StoppedStatus); QCOMPARE(recorderStatusSignal.last().first().value(), recorder.status()); } QTEST_MAIN(tst_QCameraBackend) #include "tst_qcamerabackend.moc"