diff options
Diffstat (limited to 'tests/manual')
39 files changed, 2132 insertions, 0 deletions
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt new file mode 100644 index 000000000..b37b86192 --- /dev/null +++ b/tests/manual/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +add_subdirectory(audiodecoder) +add_subdirectory(devices) +add_subdirectory(mediaformats) +add_subdirectory(minimal-player) +add_subdirectory(wasm) + +if(QT_FEATURE_gstreamer) + add_subdirectory(gstreamer-custom-camera) + add_subdirectory(gstreamer-custom-camera-rtp) +endif() + +if(TARGET Qt::Quick) + add_subdirectory(qml-minimal-camera) + add_subdirectory(qml-minimal-player) + + if(QT_FEATURE_gstreamer) + add_subdirectory(qml-gstreamer-rtp) + endif() +endif() diff --git a/tests/manual/audiodecoder/CMakeLists.txt b/tests/manual/audiodecoder/CMakeLists.txt new file mode 100644 index 000000000..758c3a913 --- /dev/null +++ b/tests/manual/audiodecoder/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(audiodecoder LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/audiodecoder") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia Widgets) + +if(ANDROID) + +endif() + +qt_add_executable(audiodecoder + audiodecoder.cpp audiodecoder.h + main.cpp +) + +set_target_properties(audiodecoder PROPERTIES + WIN32_EXECUTABLE FALSE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(audiodecoder PUBLIC + Qt::Core + Qt::Gui + Qt::Multimedia +) + +if(ANDROID) + target_link_libraries(audiodecoder PUBLIC Qt::Widgets) +endif() + +install(TARGETS audiodecoder + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/audiodecoder/audiodecoder.cpp b/tests/manual/audiodecoder/audiodecoder.cpp new file mode 100644 index 000000000..a8d127ab5 --- /dev/null +++ b/tests/manual/audiodecoder/audiodecoder.cpp @@ -0,0 +1,162 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "audiodecoder.h" + +#include <QFile> + +#include <stdio.h> + +AudioDecoder::AudioDecoder(bool isPlayback, bool isDelete, const QString &targetFileName) + : m_cout(stdout, QIODevice::WriteOnly), m_targetFilename(targetFileName) +{ + m_isPlayback = isPlayback; + m_isDelete = isDelete; + + connect(&m_decoder, &QAudioDecoder::bufferReady, this, &AudioDecoder::bufferReady); + connect(&m_decoder, QOverload<QAudioDecoder::Error>::of(&QAudioDecoder::error), this, + QOverload<QAudioDecoder::Error>::of(&AudioDecoder::error)); + connect(&m_decoder, &QAudioDecoder::isDecodingChanged, this, &AudioDecoder::isDecodingChanged); + connect(&m_decoder, &QAudioDecoder::finished, this, &AudioDecoder::finished); + connect(&m_decoder, &QAudioDecoder::positionChanged, this, &AudioDecoder::updateProgress); + connect(&m_decoder, &QAudioDecoder::durationChanged, this, &AudioDecoder::updateProgress); + + connect(&m_soundEffect, &QSoundEffect::statusChanged, this, + &AudioDecoder::playbackStatusChanged); + connect(&m_soundEffect, &QSoundEffect::playingChanged, this, &AudioDecoder::playingChanged); + + m_progress = -1.0; +} + +AudioDecoder::~AudioDecoder() +{ + delete m_waveDecoder; +} + +void AudioDecoder::setSource(const QString &fileName) +{ + m_decoder.setSource(QUrl::fromLocalFile(fileName)); +} + +void AudioDecoder::start() +{ + m_decoder.start(); +} + +void AudioDecoder::stop() +{ + m_decoder.stop(); +} + +QAudioDecoder::Error AudioDecoder::getError() +{ + return m_decoder.error(); +} + +void AudioDecoder::setTargetFilename(const QString &fileName) +{ + m_targetFilename = fileName; +} + +void AudioDecoder::bufferReady() +{ + // read a buffer from audio decoder + QAudioBuffer buffer = m_decoder.read(); + if (!buffer.isValid()) + return; + + if (!m_waveDecoder) { + QIODevice *target = new QFile(m_targetFilename, this); + if (!target->open(QIODevice::WriteOnly)) { + qWarning() << "target file is not writable"; + m_decoder.stop(); + return; + } + m_waveDecoder = new QWaveDecoder(target, buffer.format()); + } + + if (!m_waveDecoder + || (!m_waveDecoder->isOpen() && !m_waveDecoder->open(QIODevice::WriteOnly))) { + m_decoder.stop(); + return; + } + + m_waveDecoder->write(buffer.constData<char>(), buffer.byteCount()); +} + +void AudioDecoder::error(QAudioDecoder::Error error) +{ + switch (error) { + case QAudioDecoder::NoError: + return; + case QAudioDecoder::ResourceError: + m_cout << "Resource error\n"; + break; + case QAudioDecoder::FormatError: + m_cout << "Format error\n"; + break; + case QAudioDecoder::AccessDeniedError: + m_cout << "Access denied error\n"; + break; + case QAudioDecoder::NotSupportedError: + m_cout << "Service missing error\n"; + break; + } + + emit done(); +} + +void AudioDecoder::isDecodingChanged(bool isDecoding) +{ + if (isDecoding) + m_cout << "Decoding...\n"; + else + m_cout << "Decoding stopped\n"; +} + +void AudioDecoder::finished() +{ + m_waveDecoder->close(); + m_cout << "Decoding finished\n"; + + if (m_isPlayback) { + m_cout << "Starting playback\n"; + m_soundEffect.setSource(QUrl::fromLocalFile(m_targetFilename)); + m_soundEffect.play(); + } else { + emit done(); + } +} + +void AudioDecoder::playbackStatusChanged() +{ + if (m_soundEffect.status() == QSoundEffect::Error) { + m_cout << "Playback error\n"; + emit done(); + } +} + +void AudioDecoder::playingChanged() +{ + if (!m_soundEffect.isPlaying()) { + m_cout << "Playback finished\n"; + if (m_isDelete) + QFile::remove(m_targetFilename); + emit done(); + } +} + +void AudioDecoder::updateProgress() +{ + qint64 position = m_decoder.position(); + qint64 duration = m_decoder.duration(); + qreal progress = m_progress; + if (position >= 0 && duration > 0) + progress = position / (qreal)duration; + + if (progress > m_progress + 0.1) { + m_cout << "Decoding progress: " << (int)(progress * 100.0) << "%\n"; + m_progress = progress; + } +} + diff --git a/tests/manual/audiodecoder/audiodecoder.h b/tests/manual/audiodecoder/audiodecoder.h new file mode 100644 index 000000000..25ce6501b --- /dev/null +++ b/tests/manual/audiodecoder/audiodecoder.h @@ -0,0 +1,55 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef AUDIODECODER_H +#define AUDIODECODER_H + +#include <QAudioDecoder> +#include <QSoundEffect> +#include <QTextStream> +#include <QWaveDecoder> + +class AudioDecoder : public QObject +{ + Q_OBJECT + +public: + AudioDecoder(bool isPlayback, bool isDelete, const QString &targetFileName); + ~AudioDecoder(); + + void setSource(const QString &fileName); + void start(); + void stop(); + QAudioDecoder::Error getError(); + + void setTargetFilename(const QString &fileName); + +signals: + void done(); + +public slots: + void bufferReady(); + void error(QAudioDecoder::Error error); + void isDecodingChanged(bool isDecoding); + void finished(); + + void playbackStatusChanged(); + void playingChanged(); + +private slots: + void updateProgress(); + +private: + bool m_isPlayback; + bool m_isDelete; + QAudioDecoder m_decoder; + QTextStream m_cout; + + QString m_targetFilename; + QWaveDecoder *m_waveDecoder = nullptr; + QSoundEffect m_soundEffect; + + qreal m_progress; +}; + +#endif // AUDIODECODER_H diff --git a/tests/manual/audiodecoder/audiodecoder.pro b/tests/manual/audiodecoder/audiodecoder.pro new file mode 100644 index 000000000..7870d340e --- /dev/null +++ b/tests/manual/audiodecoder/audiodecoder.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = audiodecoder + +HEADERS = \ + audiodecoder.h +SOURCES = main.cpp \ + audiodecoder.cpp + +QT += multimedia +CONFIG += console + +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audiodecoder +INSTALLS += target diff --git a/tests/manual/audiodecoder/main.cpp b/tests/manual/audiodecoder/main.cpp new file mode 100644 index 000000000..c02bdfabf --- /dev/null +++ b/tests/manual/audiodecoder/main.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "audiodecoder.h" + +#include <QCoreApplication> +#include <QDir> +#include <QFileInfo> +#include <QTextStream> +#ifdef Q_OS_ANDROID +# include <QApplication> +# include <QFileDialog> +# include <QMessageBox> +# include <QStandardPaths> +#endif + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#ifdef Q_OS_ANDROID + QApplication app(argc, argv); +#else + QCoreApplication app(argc, argv); +#endif + + QFileInfo sourceFile; + QFileInfo targetFile; + bool isPlayback = false; + bool isDelete = false; + +#ifndef Q_OS_ANDROID + QTextStream cout(stdout, QIODevice::WriteOnly); + if (app.arguments().size() < 2) { + cout << "Usage: audiodecoder [-p] [-pd] SOURCEFILE [TARGETFILE]\n"; + cout << "Set -p option if you want to play output file.\n"; + cout << "Set -pd option if you want to play output file and delete it after successful " + "playback.\n"; + cout << "Default TARGETFILE name is \"out.wav\" in the same directory as the source " + "file.\n"; + return 0; + } + + if (app.arguments().at(1) == "-p") + isPlayback = true; + else if (app.arguments().at(1) == "-pd") { + isPlayback = true; + isDelete = true; + } + + int sourceFileIndex = (isPlayback || isDelete) ? 2 : 1; + if (app.arguments().size() <= sourceFileIndex) { + cout << "Error: source filename is not specified.\n"; + return 0; + } + + sourceFile.setFile(app.arguments().at(sourceFileIndex)); + if (app.arguments().size() > sourceFileIndex + 1) + targetFile.setFile(app.arguments().at(sourceFileIndex + 1)); + else + targetFile.setFile(sourceFile.dir().absoluteFilePath("out.wav")); + +#else + + const QString message = "You will be prompted to select an audio file (e.g. mp3 or ogg format) " + "which will be decoded and played back to you."; + QMessageBox messageBox(QMessageBox::Information, "Audio Decoder", message, QMessageBox::Ok); + messageBox.exec(); + sourceFile = + QFileInfo(QFileDialog::getOpenFileName(messageBox.parentWidget(), "Select Audio File")); + auto musicPath = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); + targetFile = QFileInfo(musicPath.append("/out.wav")); + isPlayback = true; +#endif + AudioDecoder decoder(isPlayback, isDelete, targetFile.absoluteFilePath()); + QObject::connect(&decoder, &AudioDecoder::done, &app, &QCoreApplication::quit); + decoder.setSource(sourceFile.absoluteFilePath()); + decoder.start(); + if (decoder.getError() != QAudioDecoder::NoError) + return 0; + + return app.exec(); +} diff --git a/tests/manual/devices/CMakeLists.txt b/tests/manual/devices/CMakeLists.txt new file mode 100644 index 000000000..cae0c577f --- /dev/null +++ b/tests/manual/devices/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(devices LANGUAGES CXX) + +if(ANDROID OR IOS) + message(FATAL_ERROR "This is a commandline tool that is not supported on mobile platforms") +endif() + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/devices") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia) + +qt_add_executable(devices + main.cpp +) + +set_target_properties(devices PROPERTIES + WIN32_EXECUTABLE FALSE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(devices PUBLIC + Qt::Core + Qt::Gui + Qt::Multimedia +) + +install(TARGETS devices + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/devices/devices.pro b/tests/manual/devices/devices.pro new file mode 100644 index 000000000..bbc5ecea8 --- /dev/null +++ b/tests/manual/devices/devices.pro @@ -0,0 +1,12 @@ +android|ios: error("This is a commandline tool that is not supported on mobile platforms") + +TEMPLATE = app +TARGET = devices + +SOURCES = main.cpp + +QT += multimedia +CONFIG += console + +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/devices +INSTALLS += target diff --git a/tests/manual/devices/main.cpp b/tests/manual/devices/main.cpp new file mode 100644 index 000000000..67b277812 --- /dev/null +++ b/tests/manual/devices/main.cpp @@ -0,0 +1,136 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QAudioDevice> +#include <QAudioFormat> +#include <QCameraDevice> +#include <QCoreApplication> +#include <QMediaDevices> +#include <QString> +#include <QTextStream> + +#include <stdio.h> + +static QString formatToString(QAudioFormat::SampleFormat sampleFormat) +{ + switch (sampleFormat) { + case QAudioFormat::UInt8: + return "UInt8"; + case QAudioFormat::Int16: + return "Int16"; + case QAudioFormat::Int32: + return "Int32"; + case QAudioFormat::Float: + return "Float"; + default: + return "Unknown"; + } +} + +static QString positionToString(QCameraDevice::Position position) +{ + switch (position) { + case QCameraDevice::BackFace: + return "BackFace"; + case QCameraDevice::FrontFace: + return "FrontFace"; + default: + return "Unspecified"; + } +} + +static void printAudioDeviceInfo(QTextStream &out, const QAudioDevice &deviceInfo) +{ + const auto isDefault = deviceInfo.isDefault() ? "Yes" : "No"; + const auto preferredFormat = deviceInfo.preferredFormat(); + const auto supportedFormats = deviceInfo.supportedSampleFormats(); + out.setFieldWidth(30); + out.setFieldAlignment(QTextStream::AlignLeft); + out << "Name: " << deviceInfo.description() << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Id: " << QString::fromLatin1(deviceInfo.id()) << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Default: " << isDefault << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Preferred Format: " << formatToString(preferredFormat.sampleFormat()) + << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Preferred Rate: " << preferredFormat.sampleRate() << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Preferred Channels: " << preferredFormat.channelCount() << qSetFieldWidth(0) + << Qt::endl; + out.setFieldWidth(30); + out << "Supported Formats: "; + for (auto &format : supportedFormats) + out << qSetFieldWidth(0) << formatToString(format) << " "; + out << Qt::endl; + out.setFieldWidth(30); + out << "Supported Rates: " << qSetFieldWidth(0) << deviceInfo.minimumSampleRate() << " - " + << deviceInfo.maximumSampleRate() << Qt::endl; + out.setFieldWidth(30); + out << "Supported Channels: " << qSetFieldWidth(0) << deviceInfo.minimumChannelCount() << " - " + << deviceInfo.maximumChannelCount() << Qt::endl; + + out << Qt::endl; +} + +static void printVideoDeviceInfo(QTextStream &out, const QCameraDevice &cameraDevice) +{ + const auto isDefault = cameraDevice.isDefault() ? "Yes" : "No"; + const auto position = cameraDevice.position(); + const auto photoResolutions = cameraDevice.photoResolutions(); + const auto videoFormats = cameraDevice.videoFormats(); + + out.setFieldWidth(30); + out.setFieldAlignment(QTextStream::AlignLeft); + out << "Name: " << cameraDevice.description() << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Id: " << QString::fromLatin1(cameraDevice.id()) << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Default: " << isDefault << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Position: " << positionToString(position) << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Photo Resolutions: "; + for (auto &resolution : photoResolutions) { + QString s = QStringLiteral("%1x%2").arg(resolution.width()).arg(resolution.height()); + out << qSetFieldWidth(0) << s << ", "; + } + out.setFieldWidth(10); + out << Qt::endl << Qt::endl; + out << "Supported Video Formats: " << qSetFieldWidth(0) << Qt::endl; + for (auto &format : videoFormats) { + out.setFieldWidth(30); + QString s = + QStringLiteral("%1x%2").arg(format.resolution().width()).arg(format.resolution().height()); + out << "Resolution: " << s << qSetFieldWidth(0) << Qt::endl; + out.setFieldWidth(30); + out << "Frame Rate: " << qSetFieldWidth(0) << "Min:" << format.minFrameRate() + << " Max:" << format.maxFrameRate() << Qt::endl; + } + + out << Qt::endl; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); // QtMultimedia needs an application singleton + + QTextStream out(stdout); + + const auto audioInputDevices = QMediaDevices::audioInputs(); + const auto audioOutputDevices = QMediaDevices::audioOutputs(); + const auto videoInputDevices = QMediaDevices::videoInputs(); + + out << "Audio devices detected: " << Qt::endl; + out << Qt::endl << "Input" << Qt::endl; + for (auto &deviceInfo : audioInputDevices) + printAudioDeviceInfo(out, deviceInfo); + out << Qt::endl << "Output" << Qt::endl; + for (auto &deviceInfo : audioOutputDevices) + printAudioDeviceInfo(out, deviceInfo); + + out << Qt::endl << "Video devices detected: " << Qt::endl; + for (auto &cameraDevice : videoInputDevices) + printVideoDeviceInfo(out, cameraDevice); +} diff --git a/tests/manual/gstreamer-custom-camera-rtp/CMakeLists.txt b/tests/manual/gstreamer-custom-camera-rtp/CMakeLists.txt new file mode 100644 index 000000000..58608db5c --- /dev/null +++ b/tests/manual/gstreamer-custom-camera-rtp/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(gstreamer-custom-camera-rtp LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/gstreamer-custom-camera-rtp") + +find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia MultimediaWidgets QGstreamerMediaPluginPrivate) + +qt_add_executable( gstreamer-custom-camera-rtp WIN32 MACOSX_BUNDLE + gstreamer-custom-camera-rtp.cpp + Info.plist.in +) + +target_link_libraries( gstreamer-custom-camera-rtp PUBLIC + Qt::Widgets + Qt::Multimedia + Qt::MultimediaPrivate + Qt::MultimediaWidgets + + Qt::QGstreamerMediaPluginPrivate +) + +install(TARGETS gstreamer-custom-camera-rtp + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +set_target_properties( gstreamer-custom-camera-rtp PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in +) diff --git a/tests/manual/gstreamer-custom-camera-rtp/Info.plist.in b/tests/manual/gstreamer-custom-camera-rtp/Info.plist.in new file mode 100644 index 000000000..46a9ecf2d --- /dev/null +++ b/tests/manual/gstreamer-custom-camera-rtp/Info.plist.in @@ -0,0 +1,46 @@ +<?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>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/gstreamer-custom-camera-rtp/gstreamer-custom-camera-rtp.cpp b/tests/manual/gstreamer-custom-camera-rtp/gstreamer-custom-camera-rtp.cpp new file mode 100644 index 000000000..b61df7ce9 --- /dev/null +++ b/tests/manual/gstreamer-custom-camera-rtp/gstreamer-custom-camera-rtp.cpp @@ -0,0 +1,57 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtMultimedia/QAudioOutput> +#include <QtMultimedia/private/qgstreamer_platformspecificinterface_p.h> +#include <QtMultimediaWidgets/QtMultimediaWidgets> +#include <QtWidgets/QApplication> +#include <QtCore/QCommandLineParser> + +#include <QtQGstreamerMediaPlugin/private/qgst_p.h> +#include <QtQGstreamerMediaPlugin/private/qgstpipeline_p.h> + +using namespace std::chrono_literals; +using namespace Qt::Literals; + +struct GStreamerRtpStreamSender +{ + GStreamerRtpStreamSender() + { + element = QGstElement::createFromPipelineDescription( + "videotestsrc ! jpegenc ! rtpjpegpay ! udpsink host=127.0.0.1 port=50004"_ba); + + pipeline.add(element); + pipeline.setStateSync(GstState::GST_STATE_PLAYING); + pipeline.dumpGraph("sender"); + } + + ~GStreamerRtpStreamSender() { pipeline.setStateSync(GstState::GST_STATE_NULL); } + + QGstPipeline pipeline = QGstPipeline::create("UdpSend"); + QGstElement element; +}; + +int main(int argc, char **argv) +{ + qputenv("QT_MEDIA_BACKEND", "gstreamer"); + + gst_init(&argc, &argv); + GStreamerRtpStreamSender sender; + + QApplication app(argc, argv); + + QByteArray pipelineString = + R"(udpsrc port=50004 ! application/x-rtp,encoding=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! videoconvert)"_ba; + QVideoWidget wid; + wid.show(); + + QMediaCaptureSession session; + session.setVideoSink(wid.videoSink()); + + QCamera *cam = QGStreamerPlatformSpecificInterface::instance()->makeCustomGStreamerCamera( + pipelineString, &session); + session.setCamera(cam); + cam->start(); + + return QApplication::exec(); +} diff --git a/tests/manual/gstreamer-custom-camera/CMakeLists.txt b/tests/manual/gstreamer-custom-camera/CMakeLists.txt new file mode 100644 index 000000000..f161a0630 --- /dev/null +++ b/tests/manual/gstreamer-custom-camera/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(gstreamer-custom-camera-rtp LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/gstreamer-custom-camera") + +find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia MultimediaWidgets) + +qt_add_executable( gstreamer-custom-camera WIN32 MACOSX_BUNDLE + gstreamer-custom-camera.cpp + Info.plist.in +) + +target_link_libraries( gstreamer-custom-camera PUBLIC + Qt::Widgets + Qt::Multimedia + Qt::MultimediaPrivate + Qt::MultimediaWidgets +) + +install(TARGETS gstreamer-custom-camera + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +set_target_properties( gstreamer-custom-camera PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in +) diff --git a/tests/manual/gstreamer-custom-camera/Info.plist.in b/tests/manual/gstreamer-custom-camera/Info.plist.in new file mode 100644 index 000000000..46a9ecf2d --- /dev/null +++ b/tests/manual/gstreamer-custom-camera/Info.plist.in @@ -0,0 +1,46 @@ +<?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>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/gstreamer-custom-camera/gstreamer-custom-camera.cpp b/tests/manual/gstreamer-custom-camera/gstreamer-custom-camera.cpp new file mode 100644 index 000000000..dbd729d15 --- /dev/null +++ b/tests/manual/gstreamer-custom-camera/gstreamer-custom-camera.cpp @@ -0,0 +1,50 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtMultimedia/QAudioOutput> +#include <QtMultimedia/private/qgstreamer_platformspecificinterface_p.h> +#include <QtMultimediaWidgets/QtMultimediaWidgets> +#include <QtWidgets/QApplication> +#include <QtCore/QCommandLineParser> + +using namespace std::chrono_literals; +using namespace Qt::Literals; + +int main(int argc, char **argv) +{ + qputenv("QT_MEDIA_BACKEND", "gstreamer"); + + QApplication app(argc, argv); + + QCommandLineParser parser; + parser.setApplicationDescription("GStreamer Custom Camera"); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument( + "pipeline", "Pipeline string, e.g. `videotestsrc pattern=smpte-rp-219 is-live=true`"); + + parser.process(app); + + QByteArray pipelineString; + + if (parser.positionalArguments().isEmpty()) { + // pipelineString = "videotestsrc pattern=smpte-rp-219 is-live=true"; + pipelineString = "videotestsrc is-live=true ! gamma gamma=2.0"; + } else { + pipelineString = parser.positionalArguments()[0].toLatin1(); + } + + QVideoWidget wid; + + QMediaCaptureSession session; + session.setVideoSink(wid.videoSink()); + + QCamera *cam = QGStreamerPlatformSpecificInterface::instance()->makeCustomGStreamerCamera( + pipelineString, &session); + session.setCamera(cam); + cam->start(); + + wid.show(); + + return QApplication::exec(); +} diff --git a/tests/manual/mediaformats/CMakeLists.txt b/tests/manual/mediaformats/CMakeLists.txt new file mode 100644 index 000000000..ed58e628e --- /dev/null +++ b/tests/manual/mediaformats/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(devices LANGUAGES CXX) + +if(ANDROID OR IOS) + message(FATAL_ERROR "This is a commandline tool that is not supported on mobile platforms") +endif() + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/mediaformats") + +find_package(Qt6 REQUIRED COMPONENTS Core Multimedia) + +qt_add_executable(mediaformats + main.cpp +) + +set_target_properties(mediaformats PROPERTIES + WIN32_EXECUTABLE FALSE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(mediaformats PUBLIC + Qt::Core + Qt::Multimedia +) + +install(TARGETS mediaformats + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/mediaformats/main.cpp b/tests/manual/mediaformats/main.cpp new file mode 100644 index 000000000..57d0ad831 --- /dev/null +++ b/tests/manual/mediaformats/main.cpp @@ -0,0 +1,87 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore/QCoreApplication> +#include <QtCore/QMimeType> +#include <QtCore/QTextStream> +#include <QtMultimedia/QMediaFormat> + +#include <stdio.h> + +namespace { + +void printFileFormatEntry(QMediaFormat::FileFormat format, QTextStream &out) +{ + out << " " << QMediaFormat::fileFormatName(format) << " - " + << QMediaFormat::fileFormatDescription(format); + + QMimeType mimeType = QMediaFormat(format).mimeType(); + if (mimeType.isValid()) { + out << " (" << mimeType.name() << ")\n"; + out << " " << mimeType.suffixes().join(", ") << "\n"; + } +} + +void printCodecEntry(QMediaFormat::AudioCodec codec, QTextStream &out) +{ + out << " " << QMediaFormat::audioCodecName(codec) << " - " + << QMediaFormat::audioCodecDescription(codec) << "\n"; +} + +void printCodecEntry(QMediaFormat::VideoCodec codec, QTextStream &out) +{ + out << " " << QMediaFormat::videoCodecName(codec) << " - " + << QMediaFormat::videoCodecDescription(codec) << "\n"; +} + +void printFileFormats(QTextStream &out) +{ + out << "Supported file formats for decoding: \n"; + for (QMediaFormat::FileFormat format : + QMediaFormat().supportedFileFormats(QMediaFormat::Decode)) + printFileFormatEntry(format, out); + + out << "\nSupported file formats for encoding: \n"; + for (QMediaFormat::FileFormat format : + QMediaFormat().supportedFileFormats(QMediaFormat::Encode)) + printFileFormatEntry(format, out); +} + +void printAudioCodecs(QTextStream &out) +{ + out << "Supported audio codecs for decoding: \n"; + for (QMediaFormat::AudioCodec codec : QMediaFormat().supportedAudioCodecs(QMediaFormat::Decode)) + printCodecEntry(codec, out); + + out << "\nSupported audio codecs for encoding: \n"; + for (QMediaFormat::AudioCodec codec : QMediaFormat().supportedAudioCodecs(QMediaFormat::Encode)) + printCodecEntry(codec, out); +} + +void printVideoCodecs(QTextStream &out) +{ + out << "Supported video codecs for decoding: \n"; + for (QMediaFormat::VideoCodec codec : QMediaFormat().supportedVideoCodecs(QMediaFormat::Decode)) + printCodecEntry(codec, out); + + out << "\nSupported video codecs for encoding: \n"; + for (QMediaFormat::VideoCodec codec : QMediaFormat().supportedVideoCodecs(QMediaFormat::Encode)) + printCodecEntry(codec, out); +} + +} // namespace + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); // QtMultimedia needs an application singleton + + QTextStream out(stdout); + + printFileFormats(out); + out << "\n"; + printAudioCodecs(out); + out << "\n"; + printVideoCodecs(out); + + return 0; +} diff --git a/tests/manual/minimal-player/CMakeLists.txt b/tests/manual/minimal-player/CMakeLists.txt new file mode 100644 index 000000000..6a5f5dfb0 --- /dev/null +++ b/tests/manual/minimal-player/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(sidepanel LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/minimal-player") + +find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia MultimediaWidgets) + +qt_add_executable( minimal-player WIN32 MACOSX_BUNDLE + minimal-player.cpp +) + +target_link_libraries( minimal-player PUBLIC + Qt::Widgets + Qt::Multimedia + Qt::MultimediaWidgets +) + +install(TARGETS minimal-player + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +set_target_properties( minimal-player PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in +) diff --git a/tests/manual/minimal-player/Info.plist.in b/tests/manual/minimal-player/Info.plist.in new file mode 100644 index 000000000..46a9ecf2d --- /dev/null +++ b/tests/manual/minimal-player/Info.plist.in @@ -0,0 +1,46 @@ +<?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>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/minimal-player/minimal-player.cpp b/tests/manual/minimal-player/minimal-player.cpp new file mode 100644 index 000000000..17a11b050 --- /dev/null +++ b/tests/manual/minimal-player/minimal-player.cpp @@ -0,0 +1,94 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore/QTimer> +#include <QtCore/QCommandLineParser> +#include <QtMultimedia/QAudioOutput> +#include <QtMultimedia/QMediaPlayer> +#include <QtMultimediaWidgets/QVideoWidget> +#include <QtWidgets/QApplication> +#include <QtWidgets/QWidget> + +using namespace std::chrono_literals; +using namespace Qt::Literals; + +int mainToggleWidgets(QString filename) +{ + QMediaPlayer player; + QVideoWidget widget1; + QVideoWidget widget2; + QAudioOutput audioOutput; + player.setVideoOutput(&widget1); + player.setAudioOutput(&audioOutput); + player.setSource(filename); + + QTimer toggleOutput; + bool toggled = {}; + + toggleOutput.callOnTimeout([&] { + toggled = !toggled; + if (toggled) + player.setVideoOutput(&widget2); + else + player.setVideoOutput(&widget1); + }); + + toggleOutput.setInterval(1s); + toggleOutput.start(); + + widget1.show(); + widget2.show(); + player.play(); + return QApplication::exec(); +} + +int mainSimple(QString filename, bool loop) +{ + QMediaPlayer player; + QVideoWidget widget1; + QAudioOutput audioOutput; + player.setVideoOutput(&widget1); + player.setAudioOutput(&audioOutput); + player.setSource(filename); + + widget1.show(); + + if (loop) + player.setLoops(QMediaPlayer::Infinite); + + player.play(); + return QApplication::exec(); +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QCommandLineParser parser; + parser.setApplicationDescription("Minimal Player"); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("media", "File to play"); + + QCommandLineOption toggleWidgetsOption{ "toggle-widgets", "Toggle between widgets." }; + parser.addOption(toggleWidgetsOption); + + QCommandLineOption loopOption{ "loop", "Loop." }; + parser.addOption(loopOption); + + parser.process(app); + + if (parser.positionalArguments().isEmpty()) { + qInfo() << "Please specify a video source"; + return 0; + } + + QString filename = parser.positionalArguments()[0]; + + if (parser.isSet(toggleWidgetsOption)) + return mainToggleWidgets(filename); + + bool loop = parser.isSet(loopOption); + + return mainSimple(filename, loop); +} diff --git a/tests/manual/qml-gstreamer-rtp/CMakeLists.txt b/tests/manual/qml-gstreamer-rtp/CMakeLists.txt new file mode 100644 index 000000000..29ea2a46a --- /dev/null +++ b/tests/manual/qml-gstreamer-rtp/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(sidepanel LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/qml-gstreamer-rtp") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia Qml Quick) + +qt_add_executable( qml-gstreamer-rtp WIN32 MACOSX_BUNDLE + qml-gstreamer-rtp.cpp +) + +qt_add_qml_module( qml-gstreamer-rtp + URI QmlMinimalplayer + NO_RESOURCE_TARGET_PATH + QML_FILES + "qml-gstreamer-rtp.qml" +) + +target_link_libraries( qml-gstreamer-rtp PUBLIC + Qt::Core + Qt::Gui + Qt::Quick +) + +install(TARGETS qml-gstreamer-rtp + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +set_target_properties( qml-gstreamer-rtp PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in +) diff --git a/tests/manual/qml-gstreamer-rtp/Info.plist.in b/tests/manual/qml-gstreamer-rtp/Info.plist.in new file mode 100644 index 000000000..46a9ecf2d --- /dev/null +++ b/tests/manual/qml-gstreamer-rtp/Info.plist.in @@ -0,0 +1,46 @@ +<?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>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.cpp b/tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.cpp new file mode 100644 index 000000000..44e8f1659 --- /dev/null +++ b/tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.cpp @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QGuiApplication> +#include <QProcess> +#include <QQmlApplicationEngine> +#include <QtEnvironmentVariables> + +int main(int argc, char *argv[]) +{ + qputenv("QT_MEDIA_BACKEND", "gstreamer"); + + QProcess process; + process.startCommand( + "gst-launch-1.0 videotestsrc ! x264enc ! udpsink host=127.0.0.1 port=50004"); + + auto scopeGuard = qScopeGuard([&] { + process.terminate(); + process.waitForFinished(); + }); + + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine; + + process.waitForStarted(); + + engine.load(QUrl("qrc:/qml-gstreamer-rtp.qml")); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); +} diff --git a/tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.qml b/tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.qml new file mode 100644 index 000000000..73ec976fb --- /dev/null +++ b/tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.qml @@ -0,0 +1,29 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +import QtQuick +import QtQuick.Controls +import QtMultimedia + +ApplicationWindow { + id: window + width: 800 + height: 600 + visible: true + title: qsTr("GStreamer RTP receiver") + + MediaPlayer { + id: player + videoOutput: output + + source: "udp://127.0.0.1:50004" + } + + Component.onCompleted: { + player.play() + } + + VideoOutput { + id: output + anchors.fill: parent + } +} diff --git a/tests/manual/qml-minimal-camera/CMakeLists.txt b/tests/manual/qml-minimal-camera/CMakeLists.txt new file mode 100644 index 000000000..fcd4676b3 --- /dev/null +++ b/tests/manual/qml-minimal-camera/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(sidepanel LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/qml-minimal-camera") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia Qml Quick) + +qt_add_executable( qml-minimal-camera WIN32 MACOSX_BUNDLE + qml-minimal-camera.cpp +) + +qt_add_qml_module( qml-minimal-camera + URI QmlMinimalCamera + NO_RESOURCE_TARGET_PATH + QML_FILES + "qml-minimal-camera.qml" +) + +target_link_libraries( qml-minimal-camera PUBLIC + Qt::Core + Qt::Gui + Qt::Quick +) + +install(TARGETS qml-minimal-camera + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +set_target_properties( qml-minimal-camera PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in +) diff --git a/tests/manual/qml-minimal-camera/Info.plist.in b/tests/manual/qml-minimal-camera/Info.plist.in new file mode 100644 index 000000000..46a9ecf2d --- /dev/null +++ b/tests/manual/qml-minimal-camera/Info.plist.in @@ -0,0 +1,46 @@ +<?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>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/qml-minimal-camera/qml-minimal-camera.cpp b/tests/manual/qml-minimal-camera/qml-minimal-camera.cpp new file mode 100644 index 000000000..c61b92992 --- /dev/null +++ b/tests/manual/qml-minimal-camera/qml-minimal-camera.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl("qrc:/qml-minimal-camera.qml")); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); +} diff --git a/tests/manual/qml-minimal-camera/qml-minimal-camera.qml b/tests/manual/qml-minimal-camera/qml-minimal-camera.qml new file mode 100644 index 000000000..3965c663e --- /dev/null +++ b/tests/manual/qml-minimal-camera/qml-minimal-camera.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls +import QtMultimedia + +ApplicationWindow { + id: window + width: 800 + height: 600 + visible: true + title: qsTr("QmlMinimalCamera") + + MediaDevices { + id: mediaDevices + } + + CaptureSession { + id: captureSession + videoOutput: output + camera: Camera { + id: camera + cameraDevice: mediaDevices.defaultVideoInput + } + + Component.onCompleted: camera.start() + } + + VideoOutput { + id: output + visible: true + } +} diff --git a/tests/manual/qml-minimal-player/CMakeLists.txt b/tests/manual/qml-minimal-player/CMakeLists.txt new file mode 100644 index 000000000..be8f61861 --- /dev/null +++ b/tests/manual/qml-minimal-player/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(sidepanel LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/qml-minimal-player") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia Qml Quick) + +qt_add_executable( qml-minimal-player WIN32 MACOSX_BUNDLE + qml-minimal-player.cpp +) + +qt_add_qml_module( qml-minimal-player + URI QmlMinimalplayer + NO_RESOURCE_TARGET_PATH + QML_FILES + "qml-minimal-player.qml" +) + +target_link_libraries( qml-minimal-player PUBLIC + Qt::Core + Qt::Gui + Qt::Quick +) + +install(TARGETS qml-minimal-player + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +set_target_properties( qml-minimal-player PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in +) diff --git a/tests/manual/qml-minimal-player/Info.plist.in b/tests/manual/qml-minimal-player/Info.plist.in new file mode 100644 index 000000000..46a9ecf2d --- /dev/null +++ b/tests/manual/qml-minimal-player/Info.plist.in @@ -0,0 +1,46 @@ +<?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>NSCameraUsageDescription</key> + <string>Qt Multimedia Example</string> + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/qml-minimal-player/qml-minimal-player.cpp b/tests/manual/qml-minimal-player/qml-minimal-player.cpp new file mode 100644 index 000000000..f7714f016 --- /dev/null +++ b/tests/manual/qml-minimal-player/qml-minimal-player.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl("qrc:/qml-minimal-player.qml")); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); +} diff --git a/tests/manual/qml-minimal-player/qml-minimal-player.qml b/tests/manual/qml-minimal-player/qml-minimal-player.qml new file mode 100644 index 000000000..4767d7e7a --- /dev/null +++ b/tests/manual/qml-minimal-player/qml-minimal-player.qml @@ -0,0 +1,42 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +import QtQuick +import QtQuick.Controls +import QtMultimedia + +ApplicationWindow { + id: window + width: 800 + height: 600 + visible: true + title: qsTr("QmlMinimalPlayer") + + MediaPlayer { + id: player + audioOutput: AudioOutput { + onMutedChanged: { + console.log("muted", player.audioOutput.muted) + } + } + videoOutput: output + + onMediaStatusChanged: { + console.log("status", player.mediaStatus); + + if (player.mediaStatus === MediaPlayer.LoadedMedia) + player.play() + } + } + + Component.onCompleted: { + if (Qt.application.arguments.length > 1) + player.setSource(Qt.application.arguments[1]) + else + console.log("Please specify a video source") + } + + VideoOutput { + id: output + visible: true + } +} diff --git a/tests/manual/wasm/CMakeLists.txt b/tests/manual/wasm/CMakeLists.txt new file mode 100644 index 000000000..1d3c623c4 --- /dev/null +++ b/tests/manual/wasm/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(QT_FEATURE_widgets) + add_subdirectory(camera) +endif() diff --git a/tests/manual/wasm/camera/CMakeLists.txt b/tests/manual/wasm/camera/CMakeLists.txt new file mode 100644 index 000000000..f63c60bbf --- /dev/null +++ b/tests/manual/wasm/camera/CMakeLists.txt @@ -0,0 +1,42 @@ +# Generated from camera-test.pro. + +cmake_minimum_required(VERSION 3.16) +project(camera-test VERSION 1.0 LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() +set(CMAKE_BUILD_TYPE Release) +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}") + +find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Multimedia MultimediaWidgets) +find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Widgets) + +qt_add_executable(camera-test WIN32 MACOSX_BUNDLE + main.cpp + mainwindow.cpp mainwindow.h mainwindow.ui +) +target_link_libraries(camera-test PUBLIC + Qt::Core + Qt::Gui + Qt::Multimedia + Qt::MultimediaWidgets + Qt::CorePrivate + Qt::Widgets +) + +set(CMAKE_BUILD_TYPE Debug) +# uncomment to enable asyncify +# target_link_options(wasm-camera PUBLIC -sASYNCIFY -Os) + +install(TARGETS camera-test + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/wasm/camera/camera-test.pro b/tests/manual/wasm/camera/camera-test.pro new file mode 100644 index 000000000..f13ba3596 --- /dev/null +++ b/tests/manual/wasm/camera/camera-test.pro @@ -0,0 +1,69 @@ +QT += core gui multimedia multimediawidgets + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 debug + +# uncomment this to use asyncify +#wasm: QMAKE_LFLAGS += -s ASYNCIFY -Os + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + mainwindow.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +macos { + PRODUCT_NAME = $$TARGET + macx-xcode: PRODUCT_NAME = $${LITERAL_DOLLAR}{PRODUCT_NAME} + INFOPLIST = \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \ + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" \ + "<plist version=\"1.0\">" \ + "<dict>" \ + " <key>CFBundleIconFile</key>" \ + " <string></string>" \ + " <key>CFBundlePackageType</key>" \ + " <string>APPL</string>" \ + " <key>CFBundleGetInfoString</key>" \ + " <string>Created by Qt/QMake</string>" \ + " <key>CFBundleSignature</key>" \ + " <string>????</string>" \ + " <key>CFBundleExecutable</key>" \ + " <string>$$TARGET</string>" \ + " <key>CFBundleIdentifier</key>" \ + " <string>com.digia.$${LITERAL_DOLLAR}{PRODUCT_NAME:rfc1034identifier}</string>" \ + " <key>CFBundleDisplayName</key>" \ + " <string>$$PRODUCT_NAME</string>" \ + " <key>CFBundleName</key>" \ + " <string>$$PRODUCT_NAME</string>" \ + " <key>CFBundleShortVersionString</key>" \ + " <string>1.0</string>" \ + " <key>CFBundleVersion</key>" \ + " <string>1.0</string>" \ + " <key>NSPrincipalClass</key>" \ + " <string>NSApplication</string>" \ + " <key>NSCameraUsageDescription</key>" \ + " <string>Qt Multimedia Example</string>" \ + " <key>NSMicrophoneUsageDescription</key>" \ + " <string>Qt Multimedia Example</string>" \ + " <key>NOTE</key>" \ + " <string>This file was generated by Qt/QMake.</string>" \ + "</dict>" \ + "</plist>" + write_file($$OUT_PWD/Info.plist, INFOPLIST)|error() + QMAKE_INFO_PLIST = $$OUT_PWD/Info.plist +} diff --git a/tests/manual/wasm/camera/main.cpp b/tests/manual/wasm/camera/main.cpp new file mode 100644 index 000000000..8370ffb87 --- /dev/null +++ b/tests/manual/wasm/camera/main.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" + +#include <QApplication> +#include <QLoggingCategory> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QLoggingCategory::setFilterRules("*.debug=false\n" + "qt.multimedia.wasm.*=true"); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/manual/wasm/camera/mainwindow.cpp b/tests/manual/wasm/camera/mainwindow.cpp new file mode 100644 index 000000000..78c6a7584 --- /dev/null +++ b/tests/manual/wasm/camera/mainwindow.cpp @@ -0,0 +1,261 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include <QAudioInput> +#include <QCamera> +#include <QCameraDevice> +#include <QGraphicsScene> +#include <QGraphicsVideoItem> +#include <QImageCapture> +#include <QMediaCaptureSession> +#include <QMediaDevices> +#include <QMediaFormat> +#include <QApplication> +#include <QTimer> +#include <QVideoWidget> +#include <QLabel> +#include <QFileDialog> +#include <QScreen> +#include <QMediaPlayer> + +#if QT_CONFIG(permissions) + #include <QPermission> +#endif + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), + ui(new Ui::MainWindow), + m_recorder(nullptr) +{ + ui->setupUi(this); + init(); +} + +MainWindow::~MainWindow() +{ + if (m_recorder) + delete m_recorder; + delete ui; +} + +void MainWindow::init() +{ +#if QT_CONFIG(permissions) + // camera + QCameraPermission cameraPermission; + switch (qApp->checkPermission(cameraPermission)) { + case Qt::PermissionStatus::Undetermined: + qApp->requestPermission(cameraPermission, this, &MainWindow::init); + return; + case Qt::PermissionStatus::Denied: + qWarning("Camera permission is not granted!"); + return; + case Qt::PermissionStatus::Granted: + break; + } + // microphone + QMicrophonePermission microphonePermission; + switch (qApp->checkPermission(microphonePermission)) { + case Qt::PermissionStatus::Undetermined: + qApp->requestPermission(microphonePermission, this, &MainWindow::init); + return; + case Qt::PermissionStatus::Denied: + qWarning("Microphone permission is not granted!"); + return; + case Qt::PermissionStatus::Granted: + break; + } +#endif + m_captureSession = new QMediaCaptureSession(this); + + m_mediaDevices = new QMediaDevices(this); + // wait until devices list is ready + connect(m_mediaDevices, &QMediaDevices::videoInputsChanged, + [this]() { doCamera(); }); +} + +void MainWindow::doCamera() +{ + m_audioInput.reset(new QAudioInput); + m_captureSession->setAudioInput(m_audioInput.get()); + const QList<QCameraDevice> cameras = QMediaDevices::videoInputs(); + + ui->camerasComboBox->clear(); + ui->camerasComboBox->setPlaceholderText("select camera"); + + for (const QCameraDevice &cameraDevice : cameras) { + if (ui->camerasComboBox->findText(cameraDevice.description()) == -1) + ui->camerasComboBox->addItem(cameraDevice.description(), cameraDevice.id()); + } + + if (cameras.count() == 0) { + qWarning() << "No camera found"; + } else { + + QGraphicsVideoItem *videoItem = new QGraphicsVideoItem(); + QGraphicsScene *scene = new QGraphicsScene(this); + m_captureSession->setVideoOutput(videoItem); // sets videoSink + + ui->graphicsView->setScene(scene); + ui->graphicsView->scene()->addItem(videoItem); + ui->graphicsView->show(); + } +} + +void MainWindow::on_startButton_clicked() +{ + m_camera.data()->start(); +} + +void MainWindow::on_stopButton_clicked() +{ + m_camera.data()->stop(); +} + +void MainWindow::on_captureButton_clicked() +{ + connect(m_camera.data(), &QCamera::errorOccurred, + [](QCamera::Error error, const QString &errorString) { + qWarning() << "Error occurred" << error << errorString; + }); + + QImageCapture *m_imageCapture = new QImageCapture(this); + connect(m_imageCapture, &QImageCapture::readyForCaptureChanged, [] (bool ready) { + + qWarning() << "MainWindow::readyForCaptureChanged" << ready; + }); + + connect(m_imageCapture, + &QImageCapture::imageCaptured, + [] (int id, const QImage &preview) { + qWarning() << "MainWindow::imageCaptured" << id << preview; + + }); + + connect(m_imageCapture, &QImageCapture::imageSaved, + [this] (int id, const QString &fileName) { + Q_UNUSED(id) + QFileInfo fi(fileName); + if (!fi.exists()) { + qWarning() << fileName << "Does not exist"; + } else { + QDialog *dlg = new QDialog(this); + dlg->setWindowTitle(fi.fileName()); + QHBoxLayout *l = new QHBoxLayout(dlg); + QLabel *label = new QLabel(this); + l->addWidget(label); + label->setPixmap(QPixmap(fileName)); + dlg->show(); + } + + }); + connect(m_imageCapture, + &QImageCapture::errorOccurred, [] + (int id, QImageCapture::Error error, const QString &errorString) { + qWarning() << "MainWindow::errorOccurred" << id << error << errorString; + }); + + m_captureSession->setImageCapture(m_imageCapture); + + // take photo + if (m_imageCapture->isReadyForCapture()) + m_imageCapture->captureToFile(QStringLiteral("/home/web_user/image.png")); +} + +void MainWindow::on_openButton_clicked() +{ + // open + QFileDialog *dialog = new QFileDialog(this); + dialog->setNameFilter(tr("All Files (*.*)")); + connect(dialog, &QFileDialog::fileSelected, + [this](const QString &file) { + qWarning() << "open this file" << file; + showFile(file); + }); + + dialog->show(); +} + +void MainWindow::on_camerasComboBox_textActivated(const QString &arg1) +{ + const QList<QCameraDevice> cameras = QMediaDevices::videoInputs(); + for (const QCameraDevice &cameraDevice :cameras) { + if (arg1 == cameraDevice.description()) { + m_camera.reset(new QCamera(cameraDevice)); + connect(m_captureSession, &QMediaCaptureSession::cameraChanged, + [this]() { + enableButtons(true); + }); + m_captureSession->setCamera(m_camera.data()); + break; + } + } +} + +void MainWindow::on_recordButton_clicked() +{ + if (!isRecording) { + if (m_recorder) + delete m_recorder; + m_recorder = new QMediaRecorder(); + connect(m_recorder, &QMediaRecorder::durationChanged, + [](qint64 duration) { + qWarning() << "MainWindow::durationChanged" + << duration; + }); + m_captureSession->setRecorder(m_recorder); + + m_recorder->setQuality(QMediaRecorder::HighQuality); + m_recorder->setOutputLocation(QUrl::fromLocalFile("test.mp4")); + m_recorder->record(); + isRecording = true; + ui->recordButton->setText(QStringLiteral("Stop")); + } else { + m_recorder->stop(); + isRecording = false; + m_recorder->deleteLater(); + ui->recordButton->setText(QStringLiteral("Record")); + } +} + +void MainWindow::enableButtons(bool ok) +{ + ui->captureButton->setEnabled(ok); + ui->startButton->setEnabled(ok); + ui->stopButton->setEnabled(ok); + ui->openButton->setEnabled(ok); + ui->recordButton->setEnabled(ok); +} + +void MainWindow::showFile(const QString &fileName) +{ + + const QSize screenGeometry = screen()->availableSize(); + QFileInfo fi(fileName); + QDialog *dlg = new QDialog(this); + dlg->setWindowTitle(fi.fileName()); + QHBoxLayout *layout = new QHBoxLayout(dlg); + QMediaPlayer *player = new QMediaPlayer(dlg); + connect(player, &QMediaPlayer::errorOccurred, [=] (QMediaPlayer::Error error, const QString &errorString) { + qWarning() << "MediaPlayer erro!:" << error << errorString; + }); + + QGraphicsVideoItem *m_videoItem = new QGraphicsVideoItem(); + QSizeF dialogSize(screenGeometry.width() / 2, screenGeometry.height() / 2); + m_videoItem->setSize(dialogSize); + player->setVideoOutput(m_videoItem); + dlg->setGeometry(20, 20, dialogSize.width() + 40, dialogSize.height() + 40); + + QGraphicsScene *scene = new QGraphicsScene(dlg); + QGraphicsView *graphicsView = new QGraphicsView(scene); + scene->addItem(m_videoItem); + layout->addWidget(graphicsView); + + player->setSource(QUrl(fileName)); + + dlg->show(); + player->play(); +} diff --git a/tests/manual/wasm/camera/mainwindow.h b/tests/manual/wasm/camera/mainwindow.h new file mode 100644 index 000000000..854c00935 --- /dev/null +++ b/tests/manual/wasm/camera/mainwindow.h @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> +#include <QImageCapture> +#include <QMediaCaptureSession> +#include <QGraphicsVideoItem> +#include <QCamera> +#include <QMediaDevices> +#include <QMediaRecorder> + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } + +class QMediaCaptureSession; +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + + void init(); + +private slots: + void doCamera(); + void on_startButton_clicked(); + void on_stopButton_clicked(); + void on_captureButton_clicked(); + void on_openButton_clicked(); + void on_recordButton_clicked(); + void on_camerasComboBox_textActivated(const QString &arg1); + +private: + Ui::MainWindow *ui; + + void enableButtons(bool ok); + void showFile(const QString &filename); + + QScopedPointer<QCamera> m_camera; + QMediaCaptureSession *m_captureSession; + QScopedPointer<QAudioInput> m_audioInput; + QMediaDevices *m_mediaDevices; + QMediaRecorder *m_recorder; + bool isRecording = false; +}; + +QT_END_NAMESPACE +#endif // MAINWINDOW_H diff --git a/tests/manual/wasm/camera/mainwindow.ui b/tests/manual/wasm/camera/mainwindow.ui new file mode 100644 index 000000000..b5de38348 --- /dev/null +++ b/tests/manual/wasm/camera/mainwindow.ui @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QComboBox" name="camerasComboBox"> + <property name="currentIndex"> + <number>-1</number> + </property> + <item> + <property name="text"> + <string/> + </property> + </item> + </widget> + </item> + <item row="1" column="0"> + <widget class="QGraphicsView" name="graphicsView"/> + </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="startButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>start camera</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="stopButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>stop camera</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="3" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="captureButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>capture photo</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="recordButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>record</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="openButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>open</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>24</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> |