diff options
Diffstat (limited to 'examples/multimedia/camera')
23 files changed, 2883 insertions, 0 deletions
diff --git a/examples/multimedia/camera/CMakeLists.txt b/examples/multimedia/camera/CMakeLists.txt new file mode 100644 index 000000000..2c2c2a935 --- /dev/null +++ b/examples/multimedia/camera/CMakeLists.txt @@ -0,0 +1,81 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(camera LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/camera") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia MultimediaWidgets Widgets) + +set(camera_form "") +set(videosettings_form "") +if(ANDROID OR IOS) + set(camera_form camera_mobile.ui) + set(videosettings_form videosettings_mobile.ui) +else() + set(camera_form camera.ui) + set(videosettings_form videosettings.ui) +endif() + +qt_add_executable(camera + MANUAL_FINALIZATION + camera.cpp camera.h ${camera_form} + imagesettings.cpp imagesettings.h imagesettings.ui + main.cpp + videosettings.cpp videosettings.h ${videosettings_form} + metadatadialog.cpp metadatadialog.h +) + +set_target_properties(camera PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +if(APPLE AND NOT IOS) + set_target_properties(camera PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in" + ) +elseif(IOS) + set_target_properties(camera PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ios/Info.plist.in" + ) +endif() + +set_property(TARGET camera APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/android) + +target_link_libraries(camera PUBLIC + Qt::Core + Qt::Gui + Qt::Multimedia + Qt::MultimediaWidgets + Qt::Widgets +) + +# Resources: +set(camera_resource_files + "images/shutter.svg" +) + +qt_add_resources(camera "camera" + PREFIX + "/" + FILES + ${camera_resource_files} +) + +qt_finalize_executable(camera) + +install(TARGETS camera + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/multimedia/camera/android/AndroidManifest.xml b/examples/multimedia/camera/android/AndroidManifest.xml new file mode 100644 index 000000000..29c4672cf --- /dev/null +++ b/examples/multimedia/camera/android/AndroidManifest.xml @@ -0,0 +1,68 @@ +<?xml version="1.0"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.qtproject.example.camera" + android:installLocation="auto" + android:versionCode="-- %%INSERT_VERSION_CODE%% --" + android:versionName="-- %%INSERT_VERSION_NAME%% --"> + <!-- The comment below will be replaced with dependencies permissions upon deployment. + Remove the comment if you do not require these default permissions. --> + <!-- %%INSERT_PERMISSIONS --> + + <!-- The comment below will be replaced with dependencies permissions upon deployment. + Remove the comment if you do not require these default features. --> + <!-- %%INSERT_FEATURES --> + + <supports-screens + android:anyDensity="true" + android:largeScreens="true" + android:normalScreens="true" + android:smallScreens="true" /> + <application + android:name="org.qtproject.qt.android.bindings.QtApplication" + android:extractNativeLibs="true" + android:hardwareAccelerated="true" + android:label="-- %%INSERT_APP_NAME%% --" + android:requestLegacyExternalStorage="true"> + <activity + android:name="org.qtproject.qt.android.bindings.QtActivity" + android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" + android:label="-- %%INSERT_APP_NAME%% --" + android:launchMode="singleTop" + android:screenOrientation="portrait"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + <!-- Application arguments --> + + <meta-data + android:name="android.app.arguments" + android:value="-- %%INSERT_APP_ARGUMENTS%% --" /> + <!-- Application arguments --> + <meta-data + android:name="android.app.lib_name" + android:value="-- %%INSERT_APP_LIB_NAME%% --" /> + + <!-- Background running --> + <!-- Warning: changing this value to true may cause unexpected crashes if the + application still try to draw after + "applicationStateChanged(Qt::ApplicationSuspended)" signal is sent! --> + <meta-data + android:name="android.app.background_running" + android:value="false" /> + <!-- Background running --> + + <!-- extract android style --> + <!-- available android:values : + * default - In most cases this will be the same as "full", but it can also be + * something else if needed, e.g., for compatibility reasons + * full - useful QWidget & Quick Controls 1 apps + * minimal - useful for Quick Controls 2 apps, it is much faster than "full" + * none - useful for apps that don't use any of the above Qt modules --> + <meta-data + android:name="android.app.extract_android_style" + android:value="minimal" /> + <!-- extract android style --> + </activity> + </application> +</manifest> diff --git a/examples/multimedia/camera/camera.cpp b/examples/multimedia/camera/camera.cpp new file mode 100644 index 000000000..201e6e985 --- /dev/null +++ b/examples/multimedia/camera/camera.cpp @@ -0,0 +1,387 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "camera.h" +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#include "ui_camera_mobile.h" +#else +#include "ui_camera.h" +#endif +#include "videosettings.h" +#include "imagesettings.h" +#include "metadatadialog.h" + +#include <QMediaRecorder> +#include <QVideoWidget> +#include <QCameraDevice> +#include <QMediaMetaData> +#include <QMediaDevices> +#include <QAudioDevice> +#include <QAudioInput> + +#include <QMessageBox> +#include <QPalette> +#include <QImage> + +#include <QtWidgets> +#include <QMediaDevices> +#include <QMediaFormat> + +Camera::Camera() + : ui(new Ui::Camera) +{ + ui->setupUi(this); + + m_audioInput.reset(new QAudioInput); + m_captureSession.setAudioInput(m_audioInput.get()); + + //Camera devices: + + videoDevicesGroup = new QActionGroup(this); + videoDevicesGroup->setExclusive(true); + updateCameras(); + connect(&m_devices, &QMediaDevices::videoInputsChanged, this, &Camera::updateCameras); + + connect(videoDevicesGroup, &QActionGroup::triggered, this, &Camera::updateCameraDevice); + connect(ui->captureWidget, &QTabWidget::currentChanged, this, &Camera::updateCaptureMode); + + connect(ui->metaDataButton, &QPushButton::clicked, this, &Camera::showMetaDataDialog); + + setCamera(QMediaDevices::defaultVideoInput()); +} + +void Camera::setCamera(const QCameraDevice &cameraDevice) +{ + m_camera.reset(new QCamera(cameraDevice)); + m_captureSession.setCamera(m_camera.data()); + + connect(m_camera.data(), &QCamera::activeChanged, this, &Camera::updateCameraActive); + connect(m_camera.data(), &QCamera::errorOccurred, this, &Camera::displayCameraError); + + if (!m_mediaRecorder) { + m_mediaRecorder.reset(new QMediaRecorder); + m_captureSession.setRecorder(m_mediaRecorder.data()); + connect(m_mediaRecorder.data(), &QMediaRecorder::recorderStateChanged, this, &Camera::updateRecorderState); + } + + m_imageCapture = new QImageCapture; + m_captureSession.setImageCapture(m_imageCapture); + + connect(m_mediaRecorder.data(), &QMediaRecorder::durationChanged, this, &Camera::updateRecordTime); + connect(m_mediaRecorder.data(), &QMediaRecorder::errorChanged, this, &Camera::displayRecorderError); + + connect(ui->exposureCompensation, &QAbstractSlider::valueChanged, this, &Camera::setExposureCompensation); + + m_captureSession.setVideoOutput(ui->viewfinder); + + updateCameraActive(m_camera->isActive()); + updateRecorderState(m_mediaRecorder->recorderState()); + + connect(m_imageCapture, &QImageCapture::readyForCaptureChanged, this, &Camera::readyForCapture); + connect(m_imageCapture, &QImageCapture::imageCaptured, this, &Camera::processCapturedImage); + connect(m_imageCapture, &QImageCapture::imageSaved, this, &Camera::imageSaved); + connect(m_imageCapture, &QImageCapture::errorOccurred, this, &Camera::displayCaptureError); + readyForCapture(m_imageCapture->isReadyForCapture()); + + updateCaptureMode(); + + if (m_camera->cameraFormat().isNull()) { + auto formats = cameraDevice.videoFormats(); + if (!formats.isEmpty()) { + // Choose a decent camera format: Maximum resolution at at least 30 FPS + // we use 29 FPS to compare against as some cameras report 29.97 FPS... + QCameraFormat bestFormat; + for (const auto &fmt : formats) { + if (bestFormat.maxFrameRate() < 29 && fmt.maxFrameRate() > bestFormat.maxFrameRate()) + bestFormat = fmt; + else if (bestFormat.maxFrameRate() == fmt.maxFrameRate() && + bestFormat.resolution().width()*bestFormat.resolution().height() < + fmt.resolution().width()*fmt.resolution().height()) + bestFormat = fmt; + } + + m_camera->setCameraFormat(bestFormat); + m_mediaRecorder->setVideoFrameRate(bestFormat.maxFrameRate()); + } + } + + m_camera->start(); +} + +void Camera::keyPressEvent(QKeyEvent * event) +{ + if (event->isAutoRepeat()) + return; + + switch (event->key()) { + case Qt::Key_CameraFocus: + displayViewfinder(); + event->accept(); + break; + case Qt::Key_Camera: + if (m_doImageCapture) { + takeImage(); + } else { + if (m_mediaRecorder->recorderState() == QMediaRecorder::RecordingState) + stop(); + else + record(); + } + event->accept(); + break; + default: + QMainWindow::keyPressEvent(event); + } +} + +void Camera::keyReleaseEvent(QKeyEvent *event) +{ + QMainWindow::keyReleaseEvent(event); +} + +void Camera::updateRecordTime() +{ + QString str = QString("Recorded %1 sec").arg(m_mediaRecorder->duration()/1000); + ui->statusbar->showMessage(str); +} + +void Camera::processCapturedImage(int requestId, const QImage& img) +{ + Q_UNUSED(requestId); + QImage scaledImage = img.scaled(ui->viewfinder->size(), + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + + ui->lastImagePreviewLabel->setPixmap(QPixmap::fromImage(scaledImage)); + + // Display captured image for 4 seconds. + displayCapturedImage(); + QTimer::singleShot(4000, this, &Camera::displayViewfinder); +} + +void Camera::configureCaptureSettings() +{ + if (m_doImageCapture) + configureImageSettings(); + else + configureVideoSettings(); +} + +void Camera::configureVideoSettings() +{ + VideoSettings settingsDialog(m_mediaRecorder.data()); + settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); + + if (settingsDialog.exec()) + settingsDialog.applySettings(); +} + +void Camera::configureImageSettings() +{ + ImageSettings settingsDialog(m_imageCapture); + settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); + + if (settingsDialog.exec()) { + settingsDialog.applyImageSettings(); + } +} + +void Camera::record() +{ + m_mediaRecorder->record(); + updateRecordTime(); +} + +void Camera::pause() +{ + m_mediaRecorder->pause(); +} + +void Camera::stop() +{ + m_mediaRecorder->stop(); +} + +void Camera::setMuted(bool muted) +{ + m_captureSession.audioInput()->setMuted(muted); +} + +void Camera::takeImage() +{ + m_isCapturingImage = true; + m_imageCapture->captureToFile(); +} + +void Camera::displayCaptureError(int id, const QImageCapture::Error error, const QString &errorString) +{ + Q_UNUSED(id); + Q_UNUSED(error); + QMessageBox::warning(this, tr("Image Capture Error"), errorString); + m_isCapturingImage = false; +} + +void Camera::startCamera() +{ + m_camera->start(); +} + +void Camera::stopCamera() +{ + m_camera->stop(); +} + +void Camera::updateCaptureMode() +{ + int tabIndex = ui->captureWidget->currentIndex(); + m_doImageCapture = (tabIndex == 0); +} + +void Camera::updateCameraActive(bool active) +{ + if (active) { + ui->actionStartCamera->setEnabled(false); + ui->actionStopCamera->setEnabled(true); + ui->captureWidget->setEnabled(true); + ui->actionSettings->setEnabled(true); + } else { + ui->actionStartCamera->setEnabled(true); + ui->actionStopCamera->setEnabled(false); + ui->captureWidget->setEnabled(false); + ui->actionSettings->setEnabled(false); + } +} + +void Camera::updateRecorderState(QMediaRecorder::RecorderState state) +{ + switch (state) { + case QMediaRecorder::StoppedState: + ui->recordButton->setEnabled(true); + ui->pauseButton->setEnabled(true); + ui->stopButton->setEnabled(false); + ui->metaDataButton->setEnabled(true); + break; + case QMediaRecorder::PausedState: + ui->recordButton->setEnabled(true); + ui->pauseButton->setEnabled(false); + ui->stopButton->setEnabled(true); + ui->metaDataButton->setEnabled(false); + break; + case QMediaRecorder::RecordingState: + ui->recordButton->setEnabled(false); + ui->pauseButton->setEnabled(true); + ui->stopButton->setEnabled(true); + ui->metaDataButton->setEnabled(false); + break; + } +} + +void Camera::setExposureCompensation(int index) +{ + m_camera->setExposureCompensation(index*0.5); +} + +void Camera::displayRecorderError() +{ + if (m_mediaRecorder->error() != QMediaRecorder::NoError) + QMessageBox::warning(this, tr("Capture Error"), m_mediaRecorder->errorString()); +} + +void Camera::displayCameraError() +{ + if (m_camera->error() != QCamera::NoError) + QMessageBox::warning(this, tr("Camera Error"), m_camera->errorString()); +} + +void Camera::updateCameraDevice(QAction *action) +{ + setCamera(qvariant_cast<QCameraDevice>(action->data())); +} + +void Camera::displayViewfinder() +{ + ui->stackedWidget->setCurrentIndex(0); +} + +void Camera::displayCapturedImage() +{ + ui->stackedWidget->setCurrentIndex(1); +} + +void Camera::readyForCapture(bool ready) +{ + ui->takeImageButton->setEnabled(ready); +} + +void Camera::imageSaved(int id, const QString &fileName) +{ + Q_UNUSED(id); + ui->statusbar->showMessage(tr("Captured \"%1\"").arg(QDir::toNativeSeparators(fileName))); + + m_isCapturingImage = false; + if (m_applicationExiting) + close(); +} + +void Camera::closeEvent(QCloseEvent *event) +{ + if (m_isCapturingImage) { + setEnabled(false); + m_applicationExiting = true; + event->ignore(); + } else { + event->accept(); + } +} + +void Camera::updateCameras() +{ + ui->menuDevices->clear(); + const QList<QCameraDevice> availableCameras = QMediaDevices::videoInputs(); + for (const QCameraDevice &cameraDevice : availableCameras) { + QAction *videoDeviceAction = new QAction(cameraDevice.description(), videoDevicesGroup); + videoDeviceAction->setCheckable(true); + videoDeviceAction->setData(QVariant::fromValue(cameraDevice)); + if (cameraDevice == QMediaDevices::defaultVideoInput()) + videoDeviceAction->setChecked(true); + + ui->menuDevices->addAction(videoDeviceAction); + } +} + +void Camera::showMetaDataDialog() +{ + if (!m_metaDataDialog) + m_metaDataDialog = new MetaDataDialog(this); + m_metaDataDialog->setAttribute(Qt::WA_DeleteOnClose, false); + if (m_metaDataDialog->exec() == QDialog::Accepted) + saveMetaData(); +} + +void Camera::saveMetaData() +{ + QMediaMetaData data; + for (int i = 0; i < QMediaMetaData::NumMetaData; i++) { + QString val = m_metaDataDialog->m_metaDataFields[i]->text(); + if (!val.isEmpty()) { + auto key = static_cast<QMediaMetaData::Key>(i); + if (i == QMediaMetaData::CoverArtImage) { + QImage coverArt(val); + data.insert(key, coverArt); + } + else if (i == QMediaMetaData::ThumbnailImage) { + QImage thumbnail(val); + data.insert(key, thumbnail); + } + else if (i == QMediaMetaData::Date) { + QDateTime date = QDateTime::fromString(val); + data.insert(key, date); + } + else { + data.insert(key, val); + } + } + } + m_mediaRecorder->setMetaData(data); +} + diff --git a/examples/multimedia/camera/camera.h b/examples/multimedia/camera/camera.h new file mode 100644 index 000000000..ae8eb5919 --- /dev/null +++ b/examples/multimedia/camera/camera.h @@ -0,0 +1,101 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef CAMERA_H +#define CAMERA_H + +#include <QCamera> +#include <QImageCapture> +#include <QMediaRecorder> +#include <QScopedPointer> +#include <QMediaMetaData> +#include <QMediaCaptureSession> +#include <QMediaDevices> +#include <QAudioInput> + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { class Camera; } +class QActionGroup; +QT_END_NAMESPACE + +class MetaDataDialog; + +class Camera : public QMainWindow +{ + Q_OBJECT + +public: + Camera(); + +public slots: + void saveMetaData(); + +private slots: + void setCamera(const QCameraDevice &cameraDevice); + + void startCamera(); + void stopCamera(); + + void record(); + void pause(); + void stop(); + void setMuted(bool); + + void takeImage(); + void displayCaptureError(int, QImageCapture::Error, const QString &errorString); + + void configureCaptureSettings(); + void configureVideoSettings(); + void configureImageSettings(); + + void displayRecorderError(); + void displayCameraError(); + + void updateCameraDevice(QAction *action); + + void updateCameraActive(bool active); + void updateCaptureMode(); + void updateRecorderState(QMediaRecorder::RecorderState state); + void setExposureCompensation(int index); + + void updateRecordTime(); + + void processCapturedImage(int requestId, const QImage &img); + + void displayViewfinder(); + void displayCapturedImage(); + + void readyForCapture(bool ready); + void imageSaved(int id, const QString &fileName); + + void updateCameras(); + + void showMetaDataDialog(); + +protected: + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void closeEvent(QCloseEvent *event) override; + +private: + Ui::Camera *ui; + + QActionGroup *videoDevicesGroup = nullptr; + + QMediaDevices m_devices; + QMediaCaptureSession m_captureSession; + QScopedPointer<QCamera> m_camera; + QScopedPointer<QAudioInput> m_audioInput; + QImageCapture *m_imageCapture; + QScopedPointer<QMediaRecorder> m_mediaRecorder; + + bool m_isCapturingImage = false; + bool m_applicationExiting = false; + bool m_doImageCapture = true; + + MetaDataDialog *m_metaDataDialog = nullptr; +}; + +#endif diff --git a/examples/multimedia/camera/camera.pro b/examples/multimedia/camera/camera.pro new file mode 100644 index 000000000..283d84640 --- /dev/null +++ b/examples/multimedia/camera/camera.pro @@ -0,0 +1,40 @@ +TEMPLATE = app +TARGET = camera + +QT += multimedia multimediawidgets + +HEADERS = \ + camera.h \ + imagesettings.h \ + videosettings.h \ + metadatadialog.h + +SOURCES = \ + main.cpp \ + camera.cpp \ + imagesettings.cpp \ + videosettings.cpp \ + metadatadialog.cpp + +FORMS += \ + imagesettings.ui + +android|ios { + FORMS += \ + camera_mobile.ui \ + videosettings_mobile.ui +} else { + FORMS += \ + camera.ui \ + videosettings.ui +} +RESOURCES += camera.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/camera +INSTALLS += target + +QT += widgets +include(../../multimedia/shared/shared.pri) + +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android +OTHER_FILES += android/AndroidManifest.xml diff --git a/examples/multimedia/camera/camera.qrc b/examples/multimedia/camera/camera.qrc new file mode 100644 index 000000000..a915eb596 --- /dev/null +++ b/examples/multimedia/camera/camera.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/shutter.svg</file> +</qresource> +</RCC> diff --git a/examples/multimedia/camera/camera.ui b/examples/multimedia/camera/camera.ui new file mode 100644 index 000000000..560ee7fed --- /dev/null +++ b/examples/multimedia/camera/camera.ui @@ -0,0 +1,488 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Camera</class> + <widget class="QMainWindow" name="Camera"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>668</width> + <height>429</height> + </rect> + </property> + <property name="windowTitle"> + <string>Camera</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="1" column="1" colspan="2"> + <widget class="QTabWidget" name="captureWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Image</string> + </attribute> + <layout class="QGridLayout" name="gridLayout"> + <item row="3" column="0"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>161</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <widget class="QPushButton" name="takeImageButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Capture Photo</string> + </property> + <property name="icon"> + <iconset resource="camera.qrc"> + <normaloff>:/images/shutter.svg</normaloff>:/images/shutter.svg</iconset> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QSlider" name="exposureCompensation"> + <property name="minimum"> + <number>-4</number> + </property> + <property name="maximum"> + <number>4</number> + </property> + <property name="pageStep"> + <number>2</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Exposure Compensation:</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Video</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QPushButton" name="recordButton"> + <property name="text"> + <string>Record</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QPushButton" name="pauseButton"> + <property name="text"> + <string>Pause</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QPushButton" name="stopButton"> + <property name="text"> + <string>Stop</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>76</height> + </size> + </property> + </spacer> + </item> + <item row="4" column="0"> + <widget class="QPushButton" name="muteButton"> + <property name="text"> + <string>Mute</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QPushButton" name="metaDataButton"> + <property name="text"> + <string>Set metadata</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item row="0" column="0" rowspan="2"> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="viewfinderPage"> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <widget class="QVideoWidget" name="viewfinder" native="true"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="previewPage"> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <widget class="QLabel" name="lastImagePreviewLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::Box</enum> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>668</width> + <height>28</height> + </rect> + </property> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>File</string> + </property> + <addaction name="actionStartCamera"/> + <addaction name="actionStopCamera"/> + <addaction name="separator"/> + <addaction name="actionSettings"/> + <addaction name="separator"/> + <addaction name="actionExit"/> + </widget> + <widget class="QMenu" name="menuDevices"> + <property name="title"> + <string>Devices</string> + </property> + </widget> + <addaction name="menuFile"/> + <addaction name="menuDevices"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + <action name="actionExit"> + <property name="text"> + <string>Close</string> + </property> + </action> + <action name="actionStartCamera"> + <property name="text"> + <string>Start Camera</string> + </property> + </action> + <action name="actionStopCamera"> + <property name="text"> + <string>Stop Camera</string> + </property> + </action> + <action name="actionSettings"> + <property name="text"> + <string>Change Settings</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>QVideoWidget</class> + <extends>QWidget</extends> + <header>qvideowidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources> + <include location="camera.qrc"/> + </resources> + <connections> + <connection> + <sender>recordButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>record()</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>149</y> + </hint> + <hint type="destinationlabel"> + <x>61</x> + <y>238</y> + </hint> + </hints> + </connection> + <connection> + <sender>stopButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>stop()</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>225</y> + </hint> + <hint type="destinationlabel"> + <x>140</x> + <y>236</y> + </hint> + </hints> + </connection> + <connection> + <sender>pauseButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>pause()</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>187</y> + </hint> + <hint type="destinationlabel"> + <x>234</x> + <y>237</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionExit</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>154</x> + <y>130</y> + </hint> + </hints> + </connection> + <connection> + <sender>takeImageButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>takeImage()</slot> + <hints> + <hint type="sourcelabel"> + <x>625</x> + <y>132</y> + </hint> + <hint type="destinationlabel"> + <x>603</x> + <y>169</y> + </hint> + </hints> + </connection> + <connection> + <sender>muteButton</sender> + <signal>toggled(bool)</signal> + <receiver>Camera</receiver> + <slot>setMuted(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>377</y> + </hint> + <hint type="destinationlabel"> + <x>5</x> + <y>280</y> + </hint> + </hints> + </connection> + <connection> + <sender>exposureCompensation</sender> + <signal>valueChanged(int)</signal> + <receiver>Camera</receiver> + <slot>setExposureCompensation(int)</slot> + <hints> + <hint type="sourcelabel"> + <x>559</x> + <y>367</y> + </hint> + <hint type="destinationlabel"> + <x>665</x> + <y>365</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionSettings</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>configureCaptureSettings()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>333</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionStartCamera</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>startCamera()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>333</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionStopCamera</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>stopCamera()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>333</x> + <y>210</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <slot>record()</slot> + <slot>pause()</slot> + <slot>stop()</slot> + <slot>enablePreview(bool)</slot> + <slot>configureCaptureSettings()</slot> + <slot>takeImage()</slot> + <slot>startCamera()</slot> + <slot>toggleLock()</slot> + <slot>setMuted(bool)</slot> + <slot>stopCamera()</slot> + <slot>setExposureCompensation(int)</slot> + </slots> +</ui> diff --git a/examples/multimedia/camera/camera_mobile.ui b/examples/multimedia/camera/camera_mobile.ui new file mode 100644 index 000000000..7f269b17b --- /dev/null +++ b/examples/multimedia/camera/camera_mobile.ui @@ -0,0 +1,504 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Camera</class> + <widget class="QMainWindow" name="Camera"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>668</width> + <height>429</height> + </rect> + </property> + <property name="windowTitle"> + <string>Camera</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="1" column="1" colspan="2"> + <widget class="QTabWidget" name="captureWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Image</string> + </attribute> + <layout class="QGridLayout" name="gridLayout"> + <item row="4" column="0"> + <widget class="QSlider" name="exposureCompensation"> + <property name="minimum"> + <number>-4</number> + </property> + <property name="maximum"> + <number>4</number> + </property> + <property name="pageStep"> + <number>2</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Exposure Compensation:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QPushButton" name="takeImageButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Capture Photo</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/images/shutter.svg</normaloff>:/images/shutter.svg</iconset> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Video</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="recordButton"> + <property name="text"> + <string>Record</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pauseButton"> + <property name="text"> + <string>Pause</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="stopButton"> + <property name="text"> + <string>Stop</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>10</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="muteButton"> + <property name="text"> + <string>Mute</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="metaDataButton"> + <property name="text"> + <string>Set metadata</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + <item row="0" column="2"> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>145</red> + <green>145</green> + <blue>145</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="viewfinderPage"> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <widget class="QVideoWidget" name="viewfinder" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="previewPage"> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <widget class="QLabel" name="lastImagePreviewLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::Box</enum> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>668</width> + <height>22</height> + </rect> + </property> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>File</string> + </property> + <addaction name="actionStartCamera"/> + <addaction name="actionStopCamera"/> + <addaction name="separator"/> + <addaction name="actionSettings"/> + <addaction name="separator"/> + <addaction name="actionExit"/> + </widget> + <widget class="QMenu" name="menuDevices"> + <property name="title"> + <string>Devices</string> + </property> + </widget> + <addaction name="menuFile"/> + <addaction name="menuDevices"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + <action name="actionExit"> + <property name="text"> + <string>Close</string> + </property> + </action> + <action name="actionStartCamera"> + <property name="text"> + <string>Start Camera</string> + </property> + </action> + <action name="actionStopCamera"> + <property name="text"> + <string>Stop Camera</string> + </property> + </action> + <action name="actionSettings"> + <property name="text"> + <string>Change Settings</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>QVideoWidget</class> + <extends>QWidget</extends> + <header>qvideowidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>recordButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>record()</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>149</y> + </hint> + <hint type="destinationlabel"> + <x>61</x> + <y>238</y> + </hint> + </hints> + </connection> + <connection> + <sender>stopButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>stop()</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>225</y> + </hint> + <hint type="destinationlabel"> + <x>140</x> + <y>236</y> + </hint> + </hints> + </connection> + <connection> + <sender>pauseButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>pause()</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>187</y> + </hint> + <hint type="destinationlabel"> + <x>234</x> + <y>237</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionExit</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>154</x> + <y>130</y> + </hint> + </hints> + </connection> + <connection> + <sender>takeImageButton</sender> + <signal>clicked()</signal> + <receiver>Camera</receiver> + <slot>takeImage()</slot> + <hints> + <hint type="sourcelabel"> + <x>625</x> + <y>132</y> + </hint> + <hint type="destinationlabel"> + <x>603</x> + <y>169</y> + </hint> + </hints> + </connection> + <connection> + <sender>muteButton</sender> + <signal>toggled(bool)</signal> + <receiver>Camera</receiver> + <slot>setMuted(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>647</x> + <y>377</y> + </hint> + <hint type="destinationlabel"> + <x>5</x> + <y>280</y> + </hint> + </hints> + </connection> + <connection> + <sender>exposureCompensation</sender> + <signal>valueChanged(int)</signal> + <receiver>Camera</receiver> + <slot>setExposureCompensation(int)</slot> + <hints> + <hint type="sourcelabel"> + <x>559</x> + <y>367</y> + </hint> + <hint type="destinationlabel"> + <x>665</x> + <y>365</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionSettings</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>configureCaptureSettings()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>333</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionStartCamera</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>startCamera()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>333</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>actionStopCamera</sender> + <signal>triggered()</signal> + <receiver>Camera</receiver> + <slot>stopCamera()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>333</x> + <y>210</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <slot>record()</slot> + <slot>pause()</slot> + <slot>stop()</slot> + <slot>enablePreview(bool)</slot> + <slot>configureCaptureSettings()</slot> + <slot>takeImage()</slot> + <slot>startCamera()</slot> + <slot>toggleLock()</slot> + <slot>setMuted(bool)</slot> + <slot>stopCamera()</slot> + <slot>setExposureCompensation(int)</slot> + </slots> +</ui> diff --git a/examples/multimedia/camera/doc/images/camera-example.png b/examples/multimedia/camera/doc/images/camera-example.png Binary files differnew file mode 100644 index 000000000..12e1b5728 --- /dev/null +++ b/examples/multimedia/camera/doc/images/camera-example.png diff --git a/examples/multimedia/camera/doc/src/camera.qdoc b/examples/multimedia/camera/doc/src/camera.qdoc new file mode 100644 index 000000000..7a3b88d1b --- /dev/null +++ b/examples/multimedia/camera/doc/src/camera.qdoc @@ -0,0 +1,58 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only +/*! + +\example camera +\title Camera Example +\ingroup multimedia_examples +\ingroup video_examples +\ingroup camera_examples +\meta {tag} {widgets} +\brief Shows how to capture a still image or record video. +or video. + +The Camera Example demonstrates how you can use \l{Qt Multimedia} to implement +some basic Camera functionality to take still images and record video clips +with audio. + +\include examples-run.qdocinc + +The example implements a \c Camera class that acts as our camera interface. It +has a user interface, control functions, setting values and a means of defining +the location where the image or video clip is to be saved. It will also store +the image and video settings. + +The Camera class uses: +\list + \li An instance of \l {QCamera}, the API class interface to the hardware. + \li An instance of \l {QImageCapture} to take still images. + \li An instance of \l {QMediaRecorder} to record video. It also contains + the user interface object. +\endlist + +The Camera constructor does some basic initialization: +\list + \li The user interface is initialized. + \li UI signals are connected to slots that react to the triggering event. +\endlist +However, most of the work is done when the \e{setCamera()} function is called, +passing in a \l QCameraDevice. + +\e{setCamera()} sets up various connections between the user interface and the +functionality of the Camera class using signals and slots. It also instantiates +and initializes the \l {QCamera}, \l {QImageCapture}, and \l {QMediaRecorder} +objects mentioned above. The still and video recording visual tabs are enabled +and finally the \l {QCamera::start}{start()} function of the \l{QCamera} +object is called. + +Now that the camera is ready for user commands it waits for a suitable event. +Such an event can be a key press of either the \l {Qt::Key_CameraFocus} or +\l {Qt::Key_Camera} buttons on the application window. Camera focus will +simply display the preview and lock the camera settings. \c Key_Camera will +either call \e{takeImage()} if doing an image capture, or call +\c record() or \c stop() (if already recording) on the QMediaRecorder instance +when recording video. + +\image camera-example.png + +*/ diff --git a/examples/multimedia/camera/images/shutter.svg b/examples/multimedia/camera/images/shutter.svg new file mode 100644 index 000000000..18493361d --- /dev/null +++ b/examples/multimedia/camera/images/shutter.svg @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 23.3 19.4" style="enable-background:new 0 0 23.3 19.4;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:none;} +</style> +<g> + <path class="st0" d="M6.2,4.8H2.4c-0.2,0-0.1-0.1-0.1,0.1V17c0,0.2-0.1,0.8,0.1,0.8h3.9V4.8z"/> + <circle class="st0" cx="14" cy="11" r="4.5"/> + <path class="st0" d="M20.9,4.8h-1.8c-0.3,0-0.6-0.4-0.8-0.6l-1.7-2.4h-5.3L9.7,4.2C9.5,4.4,9.2,4.8,8.9,4.8H7.2v13h13.7 + c0.2,0,0.3-0.6,0.3-0.8V4.9C21.2,4.7,21.1,4.8,20.9,4.8z M14,16.4c-3,0-5.5-2.4-5.5-5.5c0-3,2.4-5.5,5.5-5.5c3,0,5.5,2.4,5.5,5.5 + C19.5,14,17,16.4,14,16.4z"/> + <path d="M14,5.5C11,5.5,8.6,8,8.6,11c0,3,2.4,5.5,5.5,5.5c3,0,5.5-2.4,5.5-5.5C19.5,8,17,5.5,14,5.5z M14,15.4 + c-2.5,0-4.5-2-4.5-4.5c0-2.5,2-4.5,4.5-4.5c2.5,0,4.5,2,4.5,4.5C18.5,13.4,16.5,15.4,14,15.4z"/> + <path d="M20.9,2.8h-1.3l-1.7-2.4c-0.2-0.2-0.5-0.6-0.8-0.6h-6.3c-0.3,0-0.6,0.4-0.8,0.6L8.4,2.8h-6c-1.3,0-2.1,0.8-2.1,2.1V17 + c0,1.3,0.8,2.8,2.1,2.8h18.5c1.3,0,2.3-1.5,2.3-2.8V4.9C23.2,3.6,22.2,2.8,20.9,2.8z M2.2,17V4.9c0-0.2-0.1-0.1,0.1-0.1h3.9v13H2.4 + C2.2,17.8,2.2,17.2,2.2,17z M21.2,17c0,0.2-0.1,0.8-0.3,0.8H7.2v-13h1.7c0.3,0,0.6-0.4,0.8-0.6l1.7-2.4h5.3l1.7,2.4 + c0.2,0.2,0.5,0.6,0.8,0.6h1.8c0.2,0,0.3-0.1,0.3,0.1V17z"/> +</g> +</svg> diff --git a/examples/multimedia/camera/imagesettings.cpp b/examples/multimedia/camera/imagesettings.cpp new file mode 100644 index 000000000..a107cc62d --- /dev/null +++ b/examples/multimedia/camera/imagesettings.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "imagesettings.h" +#include "ui_imagesettings.h" + +#include <QComboBox> +#include <QDebug> +#include <QImageCapture> +#include <QCamera> +#include <QMediaCaptureSession> + +ImageSettings::ImageSettings(QImageCapture *imageCapture, QWidget *parent) : + QDialog(parent), + ui(new Ui::ImageSettingsUi), + imagecapture(imageCapture) +{ + ui->setupUi(this); + + //image codecs + ui->imageCodecBox->addItem(tr("Default image format"), QVariant(QString())); + const auto supportedImageFormats = QImageCapture::supportedFormats(); + for (const auto &f : supportedImageFormats) { + QString description = QImageCapture::fileFormatDescription(f); + ui->imageCodecBox->addItem(QImageCapture::fileFormatName(f) + ": " + description, QVariant::fromValue(f)); + } + + ui->imageQualitySlider->setRange(0, int(QImageCapture::VeryHighQuality)); + + ui->imageResolutionBox->addItem(tr("Default Resolution")); + const QList<QSize> supportedResolutions = imagecapture->captureSession()->camera()->cameraDevice().photoResolutions(); + for (const QSize &resolution : supportedResolutions) { + ui->imageResolutionBox->addItem(QString("%1x%2").arg(resolution.width()).arg(resolution.height()), + QVariant(resolution)); + } + + selectComboBoxItem(ui->imageCodecBox, QVariant::fromValue(imagecapture->fileFormat())); + selectComboBoxItem(ui->imageResolutionBox, QVariant(imagecapture->resolution())); + ui->imageQualitySlider->setValue(imagecapture->quality()); +} + +ImageSettings::~ImageSettings() +{ + delete ui; +} + +void ImageSettings::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void ImageSettings::applyImageSettings() const +{ + imagecapture->setFileFormat(boxValue(ui->imageCodecBox).value<QImageCapture::FileFormat>()); + imagecapture->setQuality(QImageCapture::Quality(ui->imageQualitySlider->value())); + imagecapture->setResolution(boxValue(ui->imageResolutionBox).toSize()); +} + +QVariant ImageSettings::boxValue(const QComboBox *box) const +{ + int idx = box->currentIndex(); + if (idx == -1) + return QVariant(); + + return box->itemData(idx); +} + +void ImageSettings::selectComboBoxItem(QComboBox *box, const QVariant &value) +{ + for (int i = 0; i < box->count(); ++i) { + if (box->itemData(i) == value) { + box->setCurrentIndex(i); + break; + } + } +} diff --git a/examples/multimedia/camera/imagesettings.h b/examples/multimedia/camera/imagesettings.h new file mode 100644 index 000000000..13bd6dc4a --- /dev/null +++ b/examples/multimedia/camera/imagesettings.h @@ -0,0 +1,39 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef IMAGESETTINGS_H +#define IMAGESETTINGS_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QComboBox; +class QImageCapture; +namespace Ui { class ImageSettingsUi; } +QT_END_NAMESPACE + +class ImageSettings : public QDialog +{ + Q_OBJECT + +public: + explicit ImageSettings(QImageCapture *imageCapture, QWidget *parent = nullptr); + ~ImageSettings(); + + void applyImageSettings() const; + + QString format() const; + void setFormat(const QString &format); + +protected: + void changeEvent(QEvent *e) override; + +private: + QVariant boxValue(const QComboBox *box) const; + void selectComboBoxItem(QComboBox *box, const QVariant &value); + + Ui::ImageSettingsUi *ui; + QImageCapture *imagecapture; +}; + +#endif // IMAGESETTINGS_H diff --git a/examples/multimedia/camera/imagesettings.ui b/examples/multimedia/camera/imagesettings.ui new file mode 100644 index 000000000..8c59ca01d --- /dev/null +++ b/examples/multimedia/camera/imagesettings.ui @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ImageSettingsUi</class> + <widget class="QDialog" name="ImageSettingsUi"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>332</width> + <height>270</height> + </rect> + </property> + <property name="windowTitle"> + <string>Image Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Image</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Resolution:</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QComboBox" name="imageResolutionBox"/> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Image Format:</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="QComboBox" name="imageCodecBox"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Quality:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QSlider" name="imageQualitySlider"> + <property name="maximum"> + <number>4</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>14</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ImageSettingsUi</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>322</x> + <y>272</y> + </hint> + <hint type="destinationlabel"> + <x>44</x> + <y>230</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ImageSettingsUi</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>405</x> + <y>262</y> + </hint> + <hint type="destinationlabel"> + <x>364</x> + <y>227</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/multimedia/camera/ios/Info.plist.in b/examples/multimedia/camera/ios/Info.plist.in new file mode 100644 index 000000000..6a6b8db11 --- /dev/null +++ b/examples/multimedia/camera/ios/Info.plist.in @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + + <key>CFBundleGetInfoString</key> + <string>${MACOSX_BUNDLE_INFO_STRING}</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + + <key>LSRequiresIPhoneOS</key> + <true/> + + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + + <key>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> +</dict> +</plist> + diff --git a/examples/multimedia/camera/macos/Info.plist.in b/examples/multimedia/camera/macos/Info.plist.in new file mode 100644 index 000000000..ae2d945f1 --- /dev/null +++ b/examples/multimedia/camera/macos/Info.plist.in @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>CFBundleLongVersionString</key> + <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> + + <key>LSMinimumSystemVersion</key> + <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string> + + <key>CFBundleGetInfoString</key> + <string>${MACOSX_BUNDLE_INFO_STRING}</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + + <key>NSPrincipalClass</key> + <string>NSApplication</string> + + <key>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/examples/multimedia/camera/main.cpp b/examples/multimedia/camera/main.cpp new file mode 100644 index 000000000..50b411e4e --- /dev/null +++ b/examples/multimedia/camera/main.cpp @@ -0,0 +1,16 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "camera.h" + +#include <QtWidgets> + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + Camera camera; + camera.show(); + + return app.exec(); +}; diff --git a/examples/multimedia/camera/metadatadialog.cpp b/examples/multimedia/camera/metadatadialog.cpp new file mode 100644 index 000000000..096217014 --- /dev/null +++ b/examples/multimedia/camera/metadatadialog.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "metadatadialog.h" +#include "camera.h" + +#include <QtWidgets> +#include <QFormLayout> +#include <QMediaMetaData> + +MetaDataDialog::MetaDataDialog(QWidget *parent) + : QDialog(parent) +{ + QFormLayout *metaDataLayout = new QFormLayout; + for (int key = 0; key < QMediaMetaData::NumMetaData; key++) { + QString label = QMediaMetaData::metaDataKeyToString(static_cast<QMediaMetaData::Key>(key)); + m_metaDataFields[key] = new QLineEdit; + if (key == QMediaMetaData::ThumbnailImage) { + QPushButton *openThumbnail = new QPushButton(tr("Open")); + connect(openThumbnail, &QPushButton::clicked, this, &MetaDataDialog::openThumbnailImage); + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(m_metaDataFields[key]); + layout->addWidget(openThumbnail); + metaDataLayout->addRow(label, layout); + } + else if (key == QMediaMetaData::CoverArtImage) { + QPushButton *openCoverArt = new QPushButton(tr("Open")); + connect(openCoverArt, &QPushButton::clicked, this, &MetaDataDialog::openCoverArtImage); + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(m_metaDataFields[key]); + layout->addWidget(openCoverArt); + metaDataLayout->addRow(label, layout); + } + else { + if (key == QMediaMetaData::Title) + m_metaDataFields[key]->setText(tr("Qt Camera Example")); + else if (key == QMediaMetaData::Author) + m_metaDataFields[key]->setText(tr("The Qt Company")); + else if (key == QMediaMetaData::Date) + m_metaDataFields[key]->setText(QDateTime::currentDateTime().toString()); + else if (key == QMediaMetaData::Date) + m_metaDataFields[key]->setText(QDate::currentDate().toString()); + metaDataLayout->addRow(label, m_metaDataFields[key]); + } + } + + QWidget *viewport = new QWidget; + viewport->setLayout(metaDataLayout); + QScrollArea *scrollArea = new QScrollArea; + scrollArea->setWidget(viewport); + QVBoxLayout *dialogLayout = new QVBoxLayout(); + this->setLayout(dialogLayout); + this->layout()->addWidget(scrollArea); + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel); + this->layout()->addWidget(buttonBox); + + this->setWindowTitle(tr("Set Metadata")); + this->resize(400, 300); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &MetaDataDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &MetaDataDialog::reject); +} + +void MetaDataDialog::openThumbnailImage() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Image"), QDir::currentPath(), tr("Image Files (*.png *.jpg *.bmp)")); + if (!fileName.isEmpty()) + m_metaDataFields[QMediaMetaData::ThumbnailImage]->setText(fileName); +} + +void MetaDataDialog::openCoverArtImage() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Image"), QDir::currentPath(), tr("Image Files (*.png *.jpg *.bmp)")); + if (!fileName.isEmpty()) + m_metaDataFields[QMediaMetaData::CoverArtImage]->setText(fileName); +} diff --git a/examples/multimedia/camera/metadatadialog.h b/examples/multimedia/camera/metadatadialog.h new file mode 100644 index 000000000..5bb5a4b0b --- /dev/null +++ b/examples/multimedia/camera/metadatadialog.h @@ -0,0 +1,31 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef DIALOG_H +#define DIALOG_H + +#include <QDialog> +#include <QMediaMetaData> + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +QT_END_NAMESPACE + +//! [0] +class MetaDataDialog : public QDialog +{ + Q_OBJECT + +public: + explicit MetaDataDialog(QWidget *parent = nullptr); + + QLineEdit *m_metaDataFields[QMediaMetaData::NumMetaData] = {}; + +private slots: + void openThumbnailImage(); + void openCoverArtImage(); +}; +//! [0] + +#endif diff --git a/examples/multimedia/camera/videosettings.cpp b/examples/multimedia/camera/videosettings.cpp new file mode 100644 index 000000000..b2c62bafc --- /dev/null +++ b/examples/multimedia/camera/videosettings.cpp @@ -0,0 +1,201 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "videosettings.h" +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#include "ui_videosettings_mobile.h" +#else +#include "ui_videosettings.h" +#endif + +#include <QComboBox> +#include <QSpinBox> +#include <QDebug> +#include <QMediaRecorder> +#include <QMediaFormat> +#include <QAudioDevice> +#include <QMediaCaptureSession> +#include <QCameraDevice> +#include <QCamera> +#include <QAudioInput> + +QString toFormattedString(const QCameraFormat &cameraFormat) +{ + QString string; + const auto &separator = QStringLiteral(" "); + + string.append(QVideoFrameFormat::pixelFormatToString(cameraFormat.pixelFormat())); + string.append(separator); + + string.append(QString::number(cameraFormat.resolution().width())); + string.append(QStringLiteral("x")); + string.append(QString::number(cameraFormat.resolution().height())); + string.append(separator); + + string.append(QString::number(cameraFormat.minFrameRate())); + string.append(QStringLiteral("-")); + string.append(QString::number(cameraFormat.maxFrameRate())); + string.append(QStringLiteral("FPS")); + + return string; +} + +VideoSettings::VideoSettings(QMediaRecorder *mediaRecorder, QWidget *parent) + : QDialog(parent), + ui(new Ui::VideoSettingsUi), + mediaRecorder(mediaRecorder) +{ + ui->setupUi(this); + + //sample rate: + auto audioDevice = mediaRecorder->captureSession()->audioInput()->device(); + ui->audioSampleRateBox->setRange(audioDevice.minimumSampleRate(), + audioDevice.maximumSampleRate()); + + // camera format + ui->videoFormatBox->addItem(tr("Default camera format")); + + const QList<QCameraFormat> videoFormats = + mediaRecorder->captureSession()->camera()->cameraDevice().videoFormats(); + + for (const QCameraFormat &format : videoFormats) { + ui->videoFormatBox->addItem(toFormattedString(format), QVariant::fromValue(format)); + } + + connect(ui->videoFormatBox, &QComboBox::currentIndexChanged, [this](int /*index*/) { + const auto &cameraFormat = boxValue(ui->videoFormatBox).value<QCameraFormat>(); + ui->fpsSlider->setRange(cameraFormat.minFrameRate(), cameraFormat.maxFrameRate()); + ui->fpsSpinBox->setRange(cameraFormat.minFrameRate(), cameraFormat.maxFrameRate()); + }); + + auto currentCameraFormat = mediaRecorder->captureSession()->camera()->cameraFormat(); + ui->fpsSlider->setRange(currentCameraFormat.minFrameRate(), currentCameraFormat.maxFrameRate()); + ui->fpsSpinBox->setRange(currentCameraFormat.minFrameRate(), + currentCameraFormat.maxFrameRate()); + + connect(ui->fpsSlider, &QSlider::valueChanged, ui->fpsSpinBox, &QSpinBox::setValue); + connect(ui->fpsSpinBox, &QSpinBox::valueChanged, ui->fpsSlider, &QSlider::setValue); + + updateFormatsAndCodecs(); + connect(ui->audioCodecBox, &QComboBox::currentIndexChanged, this, &VideoSettings::updateFormatsAndCodecs); + connect(ui->videoCodecBox, &QComboBox::currentIndexChanged, this, &VideoSettings::updateFormatsAndCodecs); + connect(ui->containerFormatBox, &QComboBox::currentIndexChanged, this, &VideoSettings::updateFormatsAndCodecs); + + ui->qualitySlider->setRange(0, int(QMediaRecorder::VeryHighQuality)); + + QMediaFormat format = mediaRecorder->mediaFormat(); + selectComboBoxItem(ui->containerFormatBox, QVariant::fromValue(format.fileFormat())); + selectComboBoxItem(ui->audioCodecBox, QVariant::fromValue(format.audioCodec())); + selectComboBoxItem(ui->videoCodecBox, QVariant::fromValue(format.videoCodec())); + + ui->qualitySlider->setValue(mediaRecorder->quality()); + ui->audioSampleRateBox->setValue(mediaRecorder->audioSampleRate()); + selectComboBoxItem( + ui->videoFormatBox, + QVariant::fromValue(mediaRecorder->captureSession()->camera()->cameraFormat())); + + ui->fpsSlider->setValue(mediaRecorder->videoFrameRate()); + ui->fpsSpinBox->setValue(mediaRecorder->videoFrameRate()); +} + +VideoSettings::~VideoSettings() +{ + delete ui; +} + +void VideoSettings::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void VideoSettings::applySettings() +{ + QMediaFormat format; + format.setFileFormat(boxValue(ui->containerFormatBox).value<QMediaFormat::FileFormat>()); + format.setAudioCodec(boxValue(ui->audioCodecBox).value<QMediaFormat::AudioCodec>()); + format.setVideoCodec(boxValue(ui->videoCodecBox).value<QMediaFormat::VideoCodec>()); + + mediaRecorder->setMediaFormat(format); + mediaRecorder->setQuality(QMediaRecorder::Quality(ui->qualitySlider->value())); + mediaRecorder->setAudioSampleRate(ui->audioSampleRateBox->value()); + + const auto &cameraFormat = boxValue(ui->videoFormatBox).value<QCameraFormat>(); + mediaRecorder->setVideoResolution(cameraFormat.resolution()); + mediaRecorder->setVideoFrameRate(ui->fpsSlider->value()); + + mediaRecorder->captureSession()->camera()->setCameraFormat(cameraFormat); +} + +void VideoSettings::updateFormatsAndCodecs() +{ + if (m_updatingFormats) + return; + m_updatingFormats = true; + + QMediaFormat format; + if (ui->containerFormatBox->count()) + format.setFileFormat(boxValue(ui->containerFormatBox).value<QMediaFormat::FileFormat>()); + if (ui->audioCodecBox->count()) + format.setAudioCodec(boxValue(ui->audioCodecBox).value<QMediaFormat::AudioCodec>()); + if (ui->videoCodecBox->count()) + format.setVideoCodec(boxValue(ui->videoCodecBox).value<QMediaFormat::VideoCodec>()); + + int currentIndex = 0; + ui->audioCodecBox->clear(); + ui->audioCodecBox->addItem(tr("Default audio codec"), QVariant::fromValue(QMediaFormat::AudioCodec::Unspecified)); + for (auto codec : format.supportedAudioCodecs(QMediaFormat::Encode)) { + if (codec == format.audioCodec()) + currentIndex = ui->audioCodecBox->count(); + ui->audioCodecBox->addItem(QMediaFormat::audioCodecDescription(codec), QVariant::fromValue(codec)); + } + ui->audioCodecBox->setCurrentIndex(currentIndex); + + currentIndex = 0; + ui->videoCodecBox->clear(); + ui->videoCodecBox->addItem(tr("Default video codec"), QVariant::fromValue(QMediaFormat::VideoCodec::Unspecified)); + for (auto codec : format.supportedVideoCodecs(QMediaFormat::Encode)) { + if (codec == format.videoCodec()) + currentIndex = ui->videoCodecBox->count(); + ui->videoCodecBox->addItem(QMediaFormat::videoCodecDescription(codec), QVariant::fromValue(codec)); + } + ui->videoCodecBox->setCurrentIndex(currentIndex); + + currentIndex = 0; + ui->containerFormatBox->clear(); + ui->containerFormatBox->addItem(tr("Default file format"), QVariant::fromValue(QMediaFormat::UnspecifiedFormat)); + for (auto container : format.supportedFileFormats(QMediaFormat::Encode)) { + if (container == format.fileFormat()) + currentIndex = ui->containerFormatBox->count(); + ui->containerFormatBox->addItem(QMediaFormat::fileFormatDescription(container), QVariant::fromValue(container)); + } + ui->containerFormatBox->setCurrentIndex(currentIndex); + + m_updatingFormats = false; + +} + +QVariant VideoSettings::boxValue(const QComboBox *box) const +{ + int idx = box->currentIndex(); + if (idx == -1) + return QVariant(); + + return box->itemData(idx); +} + +void VideoSettings::selectComboBoxItem(QComboBox *box, const QVariant &value) +{ + for (int i = 0; i < box->count(); ++i) { + if (box->itemData(i) == value) { + box->setCurrentIndex(i); + break; + } + } +} diff --git a/examples/multimedia/camera/videosettings.h b/examples/multimedia/camera/videosettings.h new file mode 100644 index 000000000..2f356d90f --- /dev/null +++ b/examples/multimedia/camera/videosettings.h @@ -0,0 +1,38 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef VIDEOSETTINGS_H +#define VIDEOSETTINGS_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QComboBox; +class QMediaRecorder; +namespace Ui { class VideoSettingsUi; } +QT_END_NAMESPACE + +class VideoSettings : public QDialog +{ + Q_OBJECT + +public: + explicit VideoSettings(QMediaRecorder *mediaRecorder, QWidget *parent = nullptr); + ~VideoSettings(); + + void applySettings(); + void updateFormatsAndCodecs(); + +protected: + void changeEvent(QEvent *e) override; + +private: + QVariant boxValue(const QComboBox*) const; + void selectComboBoxItem(QComboBox *box, const QVariant &value); + + Ui::VideoSettingsUi *ui; + QMediaRecorder *mediaRecorder; + bool m_updatingFormats = false; +}; + +#endif // VIDEOSETTINGS_H diff --git a/examples/multimedia/camera/videosettings.ui b/examples/multimedia/camera/videosettings.ui new file mode 100644 index 000000000..3c1f71f11 --- /dev/null +++ b/examples/multimedia/camera/videosettings.ui @@ -0,0 +1,213 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>VideoSettingsUi</class> + <widget class="QDialog" name="VideoSettingsUi"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>686</width> + <height>499</height> + </rect> + </property> + <property name="windowTitle"> + <string>Video Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="4" column="1"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Video</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Camera Format</string> + </property> + </widget> + </item> + <item row="5" column="0" colspan="2"> + <widget class="QComboBox" name="videoCodecBox"/> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QLabel" name="label_9"> + <property name="text"> + <string>Framerate:</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Video Codec:</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QComboBox" name="videoFormatBox"/> + </item> + <item row="3" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QSpinBox" name="fpsSpinBox"/> + </item> + <item> + <widget class="QSlider" name="fpsSlider"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QWidget" name="widget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Audio</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Audio Codec:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="audioCodecBox"/> + </item> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Sample Rate:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="audioSampleRateBox"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Quality:</string> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="qualitySlider"> + <property name="maximum"> + <number>4</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>File Format:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="containerFormatBox"/> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item row="3" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>VideoSettingsUi</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>322</x> + <y>272</y> + </hint> + <hint type="destinationlabel"> + <x>44</x> + <y>230</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>VideoSettingsUi</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>405</x> + <y>262</y> + </hint> + <hint type="destinationlabel"> + <x>364</x> + <y>227</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/multimedia/camera/videosettings_mobile.ui b/examples/multimedia/camera/videosettings_mobile.ui new file mode 100644 index 000000000..6584f07f9 --- /dev/null +++ b/examples/multimedia/camera/videosettings_mobile.ui @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>VideoSettingsUi</class> + <widget class="QDialog" name="VideoSettingsUi"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>329</width> + <height>591</height> + </rect> + </property> + <property name="windowTitle"> + <string>Video Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="2" column="0"> + <widget class="QWidget" name="widget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Audio</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Audio Codec:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="audioCodecBox"/> + </item> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Sample Rate:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="audioSampleRateBox"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Quality:</string> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="qualitySlider"> + <property name="maximum"> + <number>4</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>File Format:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="containerFormatBox"/> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item row="3" column="0"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Video</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="2" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Frames per second:</string> + </property> + </widget> + </item> + <item row="6" column="0" colspan="2"> + <widget class="QComboBox" name="videoCodecBox"/> + </item> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Camera Format:</string> + </property> + </widget> + </item> + <item row="5" column="0" colspan="2"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Video Codec:</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QComboBox" name="videoFormatBox"/> + </item> + <item row="7" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="3" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QSpinBox" name="fpsSpinBox"> + <property name="minimum"> + <number>8</number> + </property> + <property name="maximum"> + <number>30</number> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="fpsSlider"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>VideoSettingsUi</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>322</x> + <y>272</y> + </hint> + <hint type="destinationlabel"> + <x>44</x> + <y>230</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>VideoSettingsUi</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>405</x> + <y>262</y> + </hint> + <hint type="destinationlabel"> + <x>364</x> + <y>227</y> + </hint> + </hints> + </connection> + </connections> +</ui> |